Skip to content

Add output columnTypes to SqlQuery for reliable type handling in Driver Adapters #28891

@mmvsk

Description

@mmvsk

Feature Summary

Add an optional columnTypes field to the SqlQuery interface in @prisma/driver-adapter-utils, allowing the Query Engine to pass expected output column types to Driver Adapters for correct type conversion.

Use Cases & Problem Description

I'm the author of prisma-adapter-bun-sqlite, a community Driver Adapter for Bun's native SQLite. While investigating an issue, I discovered a fundamental limitation in the Driver Adapter interface that causes #28890.

The problem: Adapters receive type information for input parameters (argTypes) but not for output columns. They must infer output columnTypes from database metadata.

This breaks when the database storage type differs from the Prisma schema type:

model Post {
  id        Int      @id
  createdAt DateTime @default(now())
}

With timestampFormat: "unixepoch-ms", SQLite stores DateTime as INTEGER:

CREATE TABLE Post (
  id INTEGER PRIMARY KEY,
  createdAt INTEGER NOT NULL  -- Unix epoch ms
)

When querying:

  1. The Query Engine knows createdAt should be DateTime (from the schema)
  2. The adapter sees SQLite declared type INTEGER
  3. The adapter returns ColumnTypeEnum.Int32 instead of DateTime
  4. Result: Invalid Date for all DateTime operations

The legacy Rust query engine had full schema context. The Driver Adapter interface is more decoupled, but this decoupling breaks type handling.

Proposed Solution

Add an optional columnTypes field to SqlQuery:

export type SqlQuery = {
  sql: string
  args: Array<unknown>
  argTypes: Array<ArgType>
  /**
   * Expected output column types for this query, when known from the Prisma schema.
   * Adapters can use these hints to correctly convert result values, especially
   * when the database storage type differs from the Prisma field type.
   */
  columnTypes?: Array<ColumnType | null>
}

This field would:

  • Be optional: adapters can ignore it and use existing inference logic
  • Use the same ColumnType enum already used in SqlResultSet
  • Allow null for dynamic columns where the engine doesn't know the type

Implementation scope:

  1. @prisma/driver-adapter-utils: Add columnTypes to SqlQuery type
  2. Query Engine (WASM): Populate columnTypes when building queries
  3. Official adapters: Use columnTypes when available, fall back to inference
  4. Community adapters: Can adopt at their own pace

I'd be happy to contribute a PR for the type change if the approach looks reasonable.

Alternative Solutions

1. Heuristic detection in adapters:
Detect INTEGER values in timestamp range and convert to DateTime when unixepoch-ms is set. However, this risks converting actual integer columns incorrectly.

2. Schema file parsing:
Adapters could parse the Prisma schema file to understand column types. This adds complexity, requires file access, and duplicates Query Engine work.

3. Adapter-specific configuration:
Users manually specify which columns are DateTime. This is error-prone and defeats the purpose of schema-driven development.

None of these alternatives are reliable. Only the Query Engine has authoritative schema information, it should pass it to adapters.

Potential Considerations

  • Backward compatibility: The field is optional, so existing adapters continue to work unchanged
  • Query Engine changes: The WASM engine needs to populate the field (separate PR in prisma-engines)
  • Performance: Minimal; the Query Engine already has this information during query planning
  • Dynamic queries: $queryRaw may not have schema context; null values handle this case

Prisma Version

7.1.0

What part of Prisma does this affect?

Prisma Client

Additional Context

Evidence from prisma-engines:

In prisma-engines/libs/driver-adapters/src/types.rs, the Query struct sent to adapters:

pub struct Query {
    pub sql: String,
    pub args: Vec<JSArg>,
    pub arg_types: Vec<JSArgType>,  // Input types only
}

The engine builds typed queries via quaint::ast::Query with full schema context, but this type information is discarded when serializing to the adapter interface.

Benefits:

  1. Fixes unixepoch-ms: DateTime fields stored as INTEGER work correctly
  2. Fixes aggregate functions: _min, _max on DateTime fields return valid dates
  3. Non-breaking: Existing adapters continue unchanged
  4. Benefits all databases: MySQL, PostgreSQL, SQLite all have similar type mapping challenges
  5. Aligns with Rust engine: Native connectors have this context; adapters should too

Related:

Pre-Submission Checklist

  • I have searched existing issues to make sure this is not a duplicate
  • I have checked the Prisma roadmap to see if this is already planned
  • I have described why this belongs in Prisma Core rather than a solution in application code

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/featureA request for a new feature.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions