@@ -614,6 +614,11 @@ private function buildFilters(array $filters, array &$params, int &$paramCounter
614614 $ escaped = $ this ->esc ($ key );
615615
616616 if (is_array ($ value )) {
617+ if (empty ($ value )) {
618+ // Empty IN() is invalid SQL; match nothing
619+ $ conditions [] = '0 ' ;
620+ continue ;
621+ }
617622 $ inParts = [];
618623 foreach ($ value as $ v ) {
619624 $ pn = 'p_ ' . $ paramCounter ++;
@@ -695,9 +700,9 @@ private function parseRows(string $result): array
695700 $ row ['tags ' ] = json_decode ($ row ['tags ' ], true ) ?? [];
696701 }
697702
698- // Cast numeric fields
703+ // Cast numeric fields (use +0 to preserve decimals from avg/float aggregates)
699704 if (isset ($ row ['value ' ])) {
700- $ row ['value ' ] = ( int ) $ row ['value ' ];
705+ $ row ['value ' ] = $ row ['value ' ] + 0 ;
701706 }
702707 if (isset ($ row ['count ' ])) {
703708 $ row ['count ' ] = (int ) $ row ['count ' ];
@@ -802,10 +807,6 @@ private function insert(string $table, array $rows): void
802807 $ queryParams = ['query ' => "INSERT INTO {$ escaped } FORMAT JSONEachRow " ];
803808 $ url = "{$ scheme }:// {$ this ->host }: {$ this ->port }/? " . http_build_query ($ queryParams );
804809
805- $ this ->client ->addHeader ('X-ClickHouse-Database ' , $ this ->database );
806- $ this ->client ->addHeader ('Content-Type ' , 'application/x-ndjson ' );
807- $ this ->client ->addHeader ('Connection ' , 'keep-alive ' );
808-
809810 $ jsonRows = array_map (function ($ row ) {
810811 $ encoded = json_encode ($ row );
811812 if ($ encoded === false ) {
@@ -816,22 +817,45 @@ private function insert(string $table, array $rows): void
816817
817818 $ body = implode ("\n" , $ jsonRows );
818819
819- try {
820- $ response = $ this ->client ->fetch (
821- url: $ url ,
822- method: Client::METHOD_POST ,
823- body: $ body ,
824- );
825-
826- $ httpCode = $ response ->getStatusCode ();
827- if ($ httpCode !== 200 ) {
828- $ responseBody = $ response ->getBody ();
829- $ responseBody = is_string ($ responseBody ) ? $ responseBody : '' ;
830- throw new Exception ("ClickHouse insert HTTP {$ httpCode }: {$ responseBody }" );
820+ $ attempt = 0 ;
821+ $ lastException = null ;
822+
823+ while ($ attempt <= $ this ->maxRetries ) {
824+ try {
825+ $ this ->client ->addHeader ('X-ClickHouse-Database ' , $ this ->database );
826+ $ this ->client ->addHeader ('Content-Type ' , 'application/x-ndjson ' );
827+ $ this ->client ->addHeader ('Connection ' , 'keep-alive ' );
828+
829+ try {
830+ $ response = $ this ->client ->fetch (
831+ url: $ url ,
832+ method: Client::METHOD_POST ,
833+ body: $ body ,
834+ );
835+ } finally {
836+ $ this ->client ->removeHeader ('Content-Type ' );
837+ }
838+
839+ $ httpCode = $ response ->getStatusCode ();
840+ if ($ httpCode !== 200 ) {
841+ $ responseBody = $ response ->getBody ();
842+ $ responseBody = is_string ($ responseBody ) ? $ responseBody : '' ;
843+ throw new Exception ("ClickHouse insert HTTP {$ httpCode }: {$ responseBody }" );
844+ }
845+
846+ return ;
847+ } catch (Exception $ e ) {
848+ $ lastException = $ e ;
849+ if ($ attempt < $ this ->maxRetries && $ this ->isRetryable ($ e )) {
850+ $ attempt ++;
851+ usleep ($ this ->retryDelay * (2 ** ($ attempt - 1 )) * 1000 );
852+ continue ;
853+ }
854+ throw $ e ;
831855 }
832- } finally {
833- $ this ->client ->removeHeader ('Content-Type ' );
834856 }
857+
858+ throw $ lastException ?? new Exception ('Unknown insert error ' );
835859 }
836860
837861 // ─── Internal: Utilities ─────────────────────────────────────────
0 commit comments