Skip to content

driver-sql: the isSqlite/isPostgres/isMysql getters recognise fewer knex client spellings than knex accepts, so a valid client: 'postgres' silently loses every dialect-specific behaviour #11550

Description

@os-zhuang

Summary

SqlDriver's three dialect getters decide which dialect's behaviour applies by
matching config.client against a hard-coded pair of literals each:

protectedgetisSqlite(): boolean {constc=(this.configasany).client;returnc==='sqlite3'||c==='better-sqlite3';}protectedgetisPostgres(): boolean {constc=(this.configasany).client;returnc==='pg'||c==='postgresql';}protectedgetisMysql(): boolean {constc=(this.configasany).client;returnc==='mysql'||c==='mysql2';}

knex accepts more spellings than that. From knex/lib/constants.js (knex 3.3.0,
the version this repo pins):

constCLIENT_ALIASES=Object.freeze({pg: 'postgres',postgresql: 'postgres',sqlite: 'sqlite3'});constSUPPORTED_CLIENTS=Object.freeze(['mariadb','mssql','mysql','mysql2','oracledb','postgres','pgnative','redshift','sqlite3','cockroachdb','better-sqlite3',].concat(Object.keys(CLIENT_ALIASES)));

So client: 'postgres' — knex's own canonical name for the dialect, of which
pg and postgresql are merely the aliases — makes isPostgres return
false. Same shape for client: 'sqlite' (isSqlite → false) and for
cockroachdb / redshift, which speak the pg wire protocol.

Why it is not cosmetic

isPostgres gates real behaviour, not just logging. The clearest case is
nowColumnDefault, where the Postgres branch exists precisely to avoid a
timezone defect:

if(type==='date'){if(this.isMysql)returnthis.knex.raw('(cast(utc_timestamp() as date))');if(this.isPostgres)returnthis.knex.raw("(timezone('utc', now())::date)");}returnthis.knex.fn.now();// ← CURRENT_TIMESTAMP

With client: 'postgres' the driver falls through to knex.fn.now(), i.e. a
bare CURRENT_TIMESTAMP default on a DATE column — which resolves the
calendar day in the server's timezone, the exact defect that branch was
added to remove ("measured: a UTC-12 server records YESTERDAY", per the method's
own docblock). Nothing fails; the column is simply created with the wrong
default, on a configuration knex considers valid.

The file already disagrees with itself

DIALECT_CONNECT_TIMEOUT in the same class covers the wider set for the pg
family — pg, postgres, postgresql, cockroachdb — so two tables in one
file answer "is this Postgres?" differently depending on which one is asked.
This was measured while fixing #11389: a session pin placed behind
DIALECT_CONNECT_TIMEOUT's early return silently skipped redshift, which that
table omits while POSTGRES_WIRE_CLIENTS (added there) includes it. That fix
introduced a third list rather than widening isPostgres, deliberately — the
getters are read from regions of sql-driver.ts held by other in-flight claims,
so widening them was out of scope for that card.

Suggested direction (not a decision)

One dialect-identity source per dialect family, derived from a single table of
client spellings, with the connect-timeout table and the wire-protocol set
reading from it rather than each carrying its own literals. Whether redshift
and cockroachdb should be treated as Postgres for SQL emission (as opposed
to wire parsing) is a genuine question and probably wants a decision rather than
a guess — Redshift in particular diverges on DDL.

Where measured

packages/drivers/driver-sql/src/sql-driver.ts — the three getters, and
DIALECT_CONNECT_TIMEOUT / nowColumnDefault in the same file. Filed while
working #11389; not fixed there.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions