You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: honour the declared SQL type and reject phantom parameters
Three defects in the parameter path, fixed in this order because making an
unbound marker an error before hardening the scanner would have turned
working statements into errors.
`count_params` tracked single-quoted string literals and nothing else, so a
`?` inside a delimited identifier or a comment counted as a parameter marker:
`SELECT "a?b" FROM t` reported one parameter and `SELECT 1 -- huh?` reported
one too. `escape.rs` already scanned the same text correctly, so the fix is to
stop having a second scanner: its `copy_*` helpers split into `skip_*` plus a
copying wrapper, and `count_params` calls the same `skip_*` functions with the
identifier delimiters taken from the backend's `EscapeDialect`.
A marker with no binding was padded with `ColumnValue::Null`, so `WHERE x = ?`
with nothing bound ran as `WHERE x = NULL` — no rows, and `SQL_SUCCESS`. Both
execution paths now report 07002, the first clause of that row on the
`SQLExecute` and `SQLExecDirect` tables, neither of them `(DM)`-marked. The
data-at-execution scan rejects the same gap so it is not a second route to the
old behaviour. A `SQL_PARAM_OUTPUT` binding still yields `Null`: it has no
input value, and reading its uninitialised buffer would be unsound.
`read_param_value` matched on the C type alone, so `ParameterBinding::sql_type`
— `SQLBindParameter`'s `ParameterType` — was recorded and never read. For every
C type but the two character ones that lost nothing; for `SQL_C_CHAR` and
`SQL_C_WCHAR` it discarded the only statement of what the text was, and
`SQL_C_CHAR` + `SQL_NUMERIC` reached the backend as a string. The new
`param_convert` module is the spec's "C to SQL: Character" table transcribed,
with the SQLSTATE its third column gives for each outcome. Decimal literals are
carried as digits and a scale rather than through `f64`, so scale survives.
Driver-visible: a backend now receives `Decimal`, `I32`, `Timestamp` and so on
where a character binding previously produced `String`.
Verified with `pre-commit run --all-files` (15 hooks), 937 unit tests, and
Miri (927 passed, no leaks).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -790,6 +790,7 @@ Generic framework. Zero database-specific code.
790
790
|`types/version.rs`| Parsed data-source version numbers, for a backend gating capabilities on server version |
791
791
|`types/redacted.rs`|`Redacted<T>` — `Debug` wrapper that prints `*****` for sensitive fields (e.g. passwords) |
792
792
|`column_value.rs`|`write_column_value()` — core data marshalling for `SQLGetData` (NULL, truncation, type coercion) |
793
+
|`param_convert.rs`|`text_to_sql_type()` — the reverse direction: converts `SQL_C_CHAR`/`SQL_C_WCHAR` parameter text to the SQL type `SQLBindParameter` declared. The spec's "C to SQL: Character" table, transcribed |
793
794
|`synthetic.rs`|`SyntheticStatement` — in-memory result set for `SQLGetTypeInfo` and catalog functions |
794
795
|`catalog_sort.rs`| Sorts a catalog result set into its spec-mandated order; NULL placement from `Backend::null_collation`|
795
796
|`catalog_ident.rs`|`SQL_ATTR_METADATA_ID` identifier normalisation and the `SQLTables``TableType` value-list parser |
0 commit comments