Skip to content

Incorrect column metadata types because strcmp comparisons are inverted #305

Description

@chrispader

Summary

QueryResult.metadata[*].type is incorrect for every non-null declared SQLite type because mapSQLiteTypeToColumnType() treats a non-zero strcmp() result as a match.

Verified on main at ad8b835 (v9.7.0).

Code evidence

types.hpp#L25-L40 currently contains conditions such as:

} else if (strcmp(type, "BOOLEAN")) {
  return ColumnType::BOOLEAN;
}

strcmp() returns 0 when strings are equal. Consequently:

  • INTEGER, TEXT, BLOB, FLOAT, and any other non-BOOLEAN declaration take the first branch and report BOOLEAN.
  • BOOLEAN skips the first branch, then differs from FLOAT, so it reports NUMBER.
  • The later intended mappings are effectively unreachable.

The result is exposed through the metadata construction in operations.cpp#L247-L258.

Smallest reproducer

const db = open({ name: 'metadata.sqlite' })

db.execute(`
  CREATE TABLE values_table (
    boolean_value BOOLEAN,
    float_value FLOAT,
    integer_value INTEGER,
    text_value TEXT,
    blob_value BLOB
  )
`)

const result = db.execute('SELECT * FROM values_table')
console.log(result.metadata)

Expected declared types:

BOOLEAN, NUMBER, INT64, TEXT, ARRAY_BUFFER

Observed from the current mapping logic:

NUMBER, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN

This is distinct from #194, which reports missing metadata entries. This issue concerns the declared type values assigned to entries that are present.

Impact

Consumers cannot rely on query metadata for schema introspection, adapters, serializers, or ORMs. In particular, integer, text, and blob columns can be interpreted as booleans.

Acceptance criteria

  • Compare declared types for equality (strcmp(...) == 0) or use a less error-prone mapping.
  • Add focused coverage for BOOLEAN, FLOAT, INTEGER, TEXT, BLOB, and a null declaration (sqlite3_column_decltype() == nullptr).
  • Define and test behavior for common SQLite affinity declarations such as REAL, DOUBLE, VARCHAR(...), and INT, or document that only the current exact declarations are supported.
  • Verify both execute() and executeAsync() expose the corrected metadata.

Regression-test target

A native or Harness test that creates one column per supported declared type and asserts result.metadata[column].type for both sync and async execution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions