11package main
22
33import (
4+ "archive/tar"
45 "bytes"
6+ "compress/gzip"
57 "flag"
68 "fmt"
79 "io"
@@ -19,20 +21,18 @@ import (
1921const (
2022 // Required ClickHouse version for generating explain files
2123 // Update this when regenerating all test expectations
22- requiredVersion = "25.8.1.3073 "
23- requiredLTS = "25 .8.13.73-lts"
24+ requiredVersion = "25.8.13.73 "
25+ requiredLTS = "v25 .8.13.73-lts"
2426
25- // Download URL for the static binary
26- downloadURL = "https://github.com/ClickHouse/ClickHouse/releases/download/v25.8.1.3073 -lts/clickhouse-linux- amd64"
27+ // Download URL for the static binary package
28+ downloadURL = "https://github.com/ClickHouse/ClickHouse/releases/download/v25.8.13.73 -lts/clickhouse-common-static-25.8.13.73- amd64.tgz "
2729)
2830
2931var (
30- clickhouseDir = ".clickhouse"
31- clickhouseBin = "clickhouse"
32- pidFile = filepath .Join (clickhouseDir , "clickhouse.pid" )
33- configFile = filepath .Join (clickhouseDir , "config.xml" )
34- serverLogFile = filepath .Join (clickhouseDir , "logs" , "server.log" )
35- serverErrFile = filepath .Join (clickhouseDir , "logs" , "server.err.log" )
32+ clickhouseDir = ".clickhouse"
33+ clickhouseBin = "./clickhouse"
34+ pidFile = ".clickhouse/clickhouse.pid"
35+ configFile = ".clickhouse/config.xml"
3636)
3737
3838func main () {
@@ -64,7 +64,7 @@ func main() {
6464 }
6565
6666 if * dryRun {
67- fmt .Println ("Dry run mode - would process tests using clickhouse local " )
67+ fmt .Println ("Dry run mode - would process tests using clickhouse client " )
6868 }
6969
7070 testdataDir := "parser/testdata"
@@ -115,6 +115,11 @@ func main() {
115115
116116// ensureClickHouse ensures ClickHouse is downloaded and running with the correct version
117117func ensureClickHouse () error {
118+ // Download if binary doesn't exist or has wrong version
119+ if err := ensureBinary (); err != nil {
120+ return fmt .Errorf ("ensuring binary: %w" , err )
121+ }
122+
118123 // Check if already running with correct version
119124 if version , err := getRunningVersion (); err == nil {
120125 if version == requiredVersion {
@@ -127,11 +132,6 @@ func ensureClickHouse() error {
127132 }
128133 }
129134
130- // Download if binary doesn't exist or has wrong version
131- if err := ensureBinary (); err != nil {
132- return fmt .Errorf ("ensuring binary: %w" , err )
133- }
134-
135135 // Start the server
136136 if err := startClickHouse (); err != nil {
137137 return fmt .Errorf ("starting ClickHouse: %w" , err )
@@ -220,32 +220,28 @@ func ensureBinary() error {
220220 return fmt .Errorf ("download failed with status %d" , resp .StatusCode )
221221 }
222222
223- // Write to temporary file first
224- tmpFile := clickhouseBin + ".tmp "
225- out , err := os .Create (tmpFile )
223+ // Download to temporary file
224+ tmpTgz := clickhouseBin + ".tgz "
225+ out , err := os .Create (tmpTgz )
226226 if err != nil {
227227 return fmt .Errorf ("creating temp file: %w" , err )
228228 }
229229
230230 written , err := io .Copy (out , resp .Body )
231231 out .Close ()
232232 if err != nil {
233- os .Remove (tmpFile )
234- return fmt .Errorf ("writing binary : %w" , err )
233+ os .Remove (tmpTgz )
234+ return fmt .Errorf ("writing archive : %w" , err )
235235 }
236236
237237 fmt .Printf ("Downloaded %d bytes\n " , written )
238238
239- // Make executable and move to final location
240- if err := os .Chmod (tmpFile , 0755 ); err != nil {
241- os .Remove (tmpFile )
242- return fmt .Errorf ("chmod: %w" , err )
243- }
244-
245- if err := os .Rename (tmpFile , clickhouseBin ); err != nil {
246- os .Remove (tmpFile )
247- return fmt .Errorf ("rename: %w" , err )
239+ // Extract clickhouse binary from the tgz
240+ if err := extractClickhouseBinary (tmpTgz ); err != nil {
241+ os .Remove (tmpTgz )
242+ return fmt .Errorf ("extracting binary: %w" , err )
248243 }
244+ os .Remove (tmpTgz )
249245
250246 // Verify version
251247 version , err := getBinaryVersion ()
@@ -261,6 +257,62 @@ func ensureBinary() error {
261257 return nil
262258}
263259
260+ // extractClickhouseBinary extracts the clickhouse binary from a tgz archive
261+ func extractClickhouseBinary (tgzPath string ) error {
262+ f , err := os .Open (tgzPath )
263+ if err != nil {
264+ return err
265+ }
266+ defer f .Close ()
267+
268+ gzr , err := gzip .NewReader (f )
269+ if err != nil {
270+ return fmt .Errorf ("gzip reader: %w" , err )
271+ }
272+ defer gzr .Close ()
273+
274+ tr := tar .NewReader (gzr )
275+
276+ // Look for the clickhouse binary in the archive
277+ // It's at usr/bin/clickhouse
278+ for {
279+ header , err := tr .Next ()
280+ if err == io .EOF {
281+ break
282+ }
283+ if err != nil {
284+ return fmt .Errorf ("reading tar: %w" , err )
285+ }
286+
287+ // Check if this is the clickhouse binary (at usr/bin/clickhouse)
288+ if header .Typeflag == tar .TypeReg && strings .HasSuffix (header .Name , "/usr/bin/clickhouse" ) {
289+ fmt .Printf ("Extracting %s...\n " , header .Name )
290+
291+ tmpBin := clickhouseBin + ".tmp"
292+ outFile , err := os .OpenFile (tmpBin , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , 0755 )
293+ if err != nil {
294+ return fmt .Errorf ("creating output file: %w" , err )
295+ }
296+
297+ if _ , err := io .Copy (outFile , tr ); err != nil {
298+ outFile .Close ()
299+ os .Remove (tmpBin )
300+ return fmt .Errorf ("extracting file: %w" , err )
301+ }
302+ outFile .Close ()
303+
304+ if err := os .Rename (tmpBin , clickhouseBin ); err != nil {
305+ os .Remove (tmpBin )
306+ return fmt .Errorf ("renaming binary: %w" , err )
307+ }
308+
309+ return nil
310+ }
311+ }
312+
313+ return fmt .Errorf ("clickhouse binary not found in archive" )
314+ }
315+
264316// getBinaryVersion returns the version of the clickhouse binary
265317func getBinaryVersion () (string , error ) {
266318 cmd := exec .Command (clickhouseBin , "local" , "--query" , "SELECT version()" )
@@ -295,23 +347,46 @@ func startClickHouse() error {
295347
296348 fmt .Println ("Starting ClickHouse server..." )
297349
298- // Start the server as a daemon
299- cmd := exec .Command (clickhouseBin , "server" ,
300- "--config-file=" + configFile ,
301- "--daemon" ,
302- "--pid-file=" + pidFile )
303- cmd .Stdout = os .Stdout
304- cmd .Stderr = os .Stderr
350+ // Start the server in background using nohup-style approach
351+ cmd := exec .Command (clickhouseBin , "server" , "--config-file=" + configFile )
305352
306- if err := cmd .Run (); err != nil {
353+ // Redirect output to log files
354+ logFile , err := os .OpenFile (filepath .Join (clickhouseDir , "logs" , "clickhouse-server.log" ),
355+ os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0644 )
356+ if err != nil {
357+ return fmt .Errorf ("opening log file: %w" , err )
358+ }
359+ errLogFile , err := os .OpenFile (filepath .Join (clickhouseDir , "logs" , "clickhouse-server.err.log" ),
360+ os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0644 )
361+ if err != nil {
362+ logFile .Close ()
363+ return fmt .Errorf ("opening error log file: %w" , err )
364+ }
365+
366+ cmd .Stdout = logFile
367+ cmd .Stderr = errLogFile
368+
369+ if err := cmd .Start (); err != nil {
370+ logFile .Close ()
371+ errLogFile .Close ()
307372 return fmt .Errorf ("starting server: %w" , err )
308373 }
309374
375+ // Write PID file
376+ if err := os .WriteFile (pidFile , []byte (fmt .Sprintf ("%d\n " , cmd .Process .Pid )), 0644 ); err != nil {
377+ return fmt .Errorf ("writing PID file: %w" , err )
378+ }
379+
310380 // Wait for server to be ready
311381 fmt .Println ("Waiting for server to be ready..." )
312- for i := 0 ; i < 30 ; i ++ {
382+ for i := 0 ; i < 60 ; i ++ {
313383 time .Sleep (1 * time .Second )
314- if version , err := getRunningVersion (); err == nil {
384+ // Try to connect to the server
385+ testCmd := exec .Command (clickhouseBin , "client" , "--query" , "SELECT version()" )
386+ var stdout bytes.Buffer
387+ testCmd .Stdout = & stdout
388+ if err := testCmd .Run (); err == nil {
389+ version := strings .TrimSpace (stdout .String ())
315390 if version != requiredVersion {
316391 return fmt .Errorf ("server started with version %s but expected %s" , version , requiredVersion )
317392 }
@@ -320,6 +395,11 @@ func startClickHouse() error {
320395 }
321396 }
322397
398+ // Check logs for errors
399+ if logData , err := os .ReadFile (filepath .Join (clickhouseDir , "logs" , "clickhouse-server.err.log" )); err == nil && len (logData ) > 0 {
400+ fmt .Fprintf (os .Stderr , "Server error log:\n %s\n " , string (logData ))
401+ }
402+
323403 return fmt .Errorf ("timeout waiting for server to start" )
324404}
325405
@@ -376,22 +456,21 @@ func writeConfig() error {
376456<clickhouse>
377457 <logger>
378458 <level>information</level>
379- <log>%s/%s </log>
380- <errorlog>%s/%s </errorlog>
459+ <log>%s/.clickhouse/logs/clickhouse-server.log </log>
460+ <errorlog>%s/.clickhouse/logs/clickhouse-server.err.log </errorlog>
381461 <size>100M</size>
382462 <count>3</count>
383463 </logger>
384464
385465 <http_port>8123</http_port>
386466 <tcp_port>9000</tcp_port>
387- <mysql_port>9004</mysql_port>
388467
389468 <listen_host>127.0.0.1</listen_host>
390469
391- <path>%s/%s /data/</path>
392- <tmp_path>%s/%s /tmp/</tmp_path>
393- <user_files_path>%s/%s /user_files/</user_files_path>
394- <format_schema_path>%s/%s /format_schemas/</format_schema_path>
470+ <path>%s/.clickhouse /data/</path>
471+ <tmp_path>%s/.clickhouse /tmp/</tmp_path>
472+ <user_files_path>%s/.clickhouse /user_files/</user_files_path>
473+ <format_schema_path>%s/.clickhouse /format_schemas/</format_schema_path>
395474
396475 <mark_cache_size>5368709120</mark_cache_size>
397476
@@ -428,14 +507,7 @@ func writeConfig() error {
428507 </default>
429508 </quotas>
430509</clickhouse>
431- ` ,
432- cwd , serverLogFile ,
433- cwd , serverErrFile ,
434- cwd , clickhouseDir ,
435- cwd , clickhouseDir ,
436- cwd , clickhouseDir ,
437- cwd , clickhouseDir ,
438- )
510+ ` , cwd , cwd , cwd , cwd , cwd , cwd )
439511
440512 return os .WriteFile (configFile , []byte (config ), 0644 )
441513}
@@ -565,10 +637,10 @@ func findCommentStart(line string) int {
565637 return - 1
566638}
567639
568- // explainAST runs EXPLAIN AST on the statement using clickhouse local
640+ // explainAST runs EXPLAIN AST on the statement using clickhouse client
569641func explainAST (stmt string ) (string , error ) {
570642 query := fmt .Sprintf ("EXPLAIN AST %s" , stmt )
571- cmd := exec .Command (clickhouseBin , "local " , "--query" , query )
643+ cmd := exec .Command (clickhouseBin , "client " , "--query" , query )
572644
573645 var stdout , stderr bytes.Buffer
574646 cmd .Stdout = & stdout
0 commit comments