Summary
UPDATE ... SET serializes numeric (and boolean) values as JSON strings in the request body. APIs that enforce their schema reject the request with 400. INSERT does not have this problem.
Root cause parent: stackql/any-sdk#129 (no schema-driven coercion at body marshal time). This issue tracks the stackql-side symptom and the SET-path typing loss.
Observed with stackql v0.10.605, clickhouse provider v26.08.00442.
Repro
UPDATE clickhouse.backups.backup_configurations
SET backup_period_in_hours = 24,
backup_retention_period_in_hours = 48,
backup_start_time = '02:00'
WHERE service_id = '<service-uuid>';
With --http.log.enabled:
http request url: 'https://api.clickhouse.cloud/v1/organizations/.../services/.../backupConfiguration', method: 'PATCH'
http request body = '{"backupPeriodInHours":"24","backupRetentionPeriodInHours":"48","backupStartTime":"02:00"}'
http response status code: 400, response body: {"error":"BAD_REQUEST: request body.backupPeriodInHours: '24'","status":400}
Both fields are type: number in the provider schema. The unquoted SQL numeric literals arrive on the wire as strings.
Analysis
- INSERT is unaffected: values from the
VALUES tuple or SELECT projection go through the typed extractors in internal/stackql/parserutil/parser_util.go (ExtractAliasedValColumnData, ExtractValuesColumnData), which switch on IntVal / FloatVal / StrVal and produce typed Go values. An INSERT with the same numeric literals against clickhouse.services.services produces a correctly typed body.
- The SET path preserves the typed
*sqlparser.SQLVal into ParameterMetadata (astvisit/param_extract.go ~715), but the downstream flatten to the parameter map renders values to strings (correct for path/query parameters, wrong for JSON body properties). any-sdk then marshals what it is given (see parent issue).
- REPLACE takes SET syntax and likely shares the path; worth a regression case alongside UPDATE.
- No workaround exists inside the engine at present:
EXEC <resource>.update @@json = '{"backupPeriodInHours": 24}' fails validation via the IsFloat() bug in the parent issue.
- EXEC named
@params only bind path/query parameters, not body fields.
- DELETE / SELECT are structurally immune (no JSON body).
Suggested resolution
Primary fix in any-sdk (schema-driven coercion at body marshal, parent issue). Optionally harden the stackql SET path to preserve SQL literal types through the flatten, mirroring the INSERT extractors.
Regression cases: numeric UPDATE (this repro) and a numeric REPLACE, asserted against the emitted body via --http.log.enabled.
Summary
UPDATE ... SETserializes numeric (and boolean) values as JSON strings in the request body. APIs that enforce their schema reject the request with 400. INSERT does not have this problem.Root cause parent: stackql/any-sdk#129 (no schema-driven coercion at body marshal time). This issue tracks the stackql-side symptom and the SET-path typing loss.
Observed with stackql v0.10.605,
clickhouseproviderv26.08.00442.Repro
With
--http.log.enabled:Both fields are
type: numberin the provider schema. The unquoted SQL numeric literals arrive on the wire as strings.Analysis
VALUEStuple orSELECTprojection go through the typed extractors ininternal/stackql/parserutil/parser_util.go(ExtractAliasedValColumnData,ExtractValuesColumnData), which switch onIntVal/FloatVal/StrValand produce typed Go values. An INSERT with the same numeric literals againstclickhouse.services.servicesproduces a correctly typed body.*sqlparser.SQLValintoParameterMetadata(astvisit/param_extract.go~715), but the downstream flatten to the parameter map renders values to strings (correct for path/query parameters, wrong for JSON body properties). any-sdk then marshals what it is given (see parent issue).EXEC <resource>.update @@json = '{"backupPeriodInHours": 24}'fails validation via theIsFloat()bug in the parent issue.@paramsonly bind path/query parameters, not body fields.Suggested resolution
Primary fix in any-sdk (schema-driven coercion at body marshal, parent issue). Optionally harden the stackql SET path to preserve SQL literal types through the flatten, mirroring the INSERT extractors.
Regression cases: numeric UPDATE (this repro) and a numeric REPLACE, asserted against the emitted body via
--http.log.enabled.