Skip to content

$queryRaw returns Uint8Array instead of string for string data columns with binary collation #29237

@sakurai-ryo

Description

@sakurai-ryo

Bug description

When using $queryRaw with MySQL, string type columns with a binary collation (e.g., utf8mb4_bin) return Uint8Array instead of string.
Note that model query APIs (e.g., findMany) returns the correct string values for the same columns.

The root cause is in adapter-mariadb's mapColumnType function.
https://github.com/prisma/prisma/blob/7.4.1/packages/adapter-mariadb/src/conversion.ts#L87
It maps to ColumnTypeEnum.Bytes when the BINARY_FLAG is set in the Column Definition Flags of the COM_QUERY Response packet.
However, MySQL sets BINARY_FLAG on any column that uses a binary collation.
These are still text collations with binary comparison semantics, not actual binary data.
The check should additionally verify that the collation is BINARY (the true binary collation), as mariadb-connector-nodejs does.
https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/e71f8496803d342723dd6ee6c206ddb9616d5473/lib/cmd/decoder/text-decoder.js#L44

Column Definition Flags:
https://mariadb.com/docs/server/reference/clientserver-protocol/4-server-response-packets/result-set-packets#field-details-flag

Severity

🔹 Minor: Unexpected behavior, but does not block development

Reproduction

Reproduction Steps

  1. Create a table and insert some data.

Use the DDL instead of Prisma migrate because Prisma schema doesn't support to specify COLLATE for each column.

CREATE TABLE `sample` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `name_varchar_bin` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
    `name_char_bin` CHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
    `name_text_bin` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
    `name_varchar` VARCHAR(255) NOT NULL,
    `name_char` CHAR(255) NOT NULL,
    `name_text` TEXT NOT NULL,
    PRIMARY KEY (`id`)
);
Insert Data
INSERT INTO
    `sample` (
        `name_varchar`,
        `name_char`,
        `name_text`,
        `name_varchar_bin`,
        `name_char_bin`,
        `name_text_bin`
    )
VALUES
    (
        'test',
        'test',
        'test',
        'test',
        'test',
        'test'
    );
  1. Create a Prisma schema and reproduction code
generator client {
  provider   = "prisma-client"
  output     = "./client"
  engineType = "client"
  runtime    = "nodejs"
}

datasource db {
  provider = "mysql"
}

model sample {
  id               BigInt @id @default(autoincrement())
  name_varchar_bin String
  name_char_bin    String
  name_text_bin    String
  name_varchar     String
  name_char        String
  name_text        String
}
import { PrismaClient } from "./client/client.ts";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";

const client = new PrismaClient({
  adapter: new PrismaMariaDb({
    user: "",
    password: "",
    database: "",
  }),
});

const many = await client.sample.findMany();
// Woks as expected
console.log("many: ", many);

const res = await client.$queryRaw`SELECT * FROM sample`;
console.log("raw: ", res);

await client.$disconnect();
  1. Run the code and check the output

The findMany query returns the expected string values, while the $queryRaw returns Uint8Array for the binary collation columns.

$ node script.ts
many:  [
  {
    id: 1n,
    name_varchar_bin: 'test',
    name_char_bin: 'test',
    name_text_bin: 'test',
    name_varchar: 'test',
    name_char: 'test',
    name_text: 'test'
  }
]
raw:  [
  {
    id: 1n,
    name_varchar_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_char_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_text_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_varchar: 'test',
    name_char: 'test',
    name_text: 'test'
  }
]

Expected vs. Actual Behavior

The expected behavior is that both findMany and $queryRaw return string values for the columns with binary collations.
The actual behavior is that findMany returns strings, while $queryRaw returns Uint8Array for those columns.

Frequency

Consistently reproducible

Does this occur in development or production?

Both development and production

Is this a regression?

No.
It seems that Prisma v6 resulted in an error rather than returning a Uint8Array.

Workaround

A workaround is to convert the collation of columns to a non-binary collation.

SELECT CONVERT(hoge USING utf8mb4) AS hoge FROM sample;

Prisma Schema & Queries

generator client {
  provider   = "prisma-client"
  output     = "./client"
  engineType = "client"
  runtime    = "nodejs"
}

datasource db {
  provider = "mysql"
}

model sample {
  id               BigInt @id @default(autoincrement())
  name_varchar_bin String
  name_char_bin    String
  name_text_bin    String
  name_varchar     String
  name_char        String
  name_text        String
  data             Bytes
}
import { PrismaClient } from "./client/client.ts";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";

const client = new PrismaClient({
  adapter: new PrismaMariaDb({
    user: "root",
    password: "root",
    database: "app",
  }),
});

const many = await client.sample.findMany();
console.log("many: ", many);

const res = await client.$queryRaw`SELECT * FROM sample`;
console.log("raw: ", res);

await client.$disconnect();

Prisma Config

No response

Logs & Debug Info

prisma:client clientVersion 7.4.1 +48ms
prisma:client:clientEngine Using driver adapter: '{\n  "provider": "mysql",\n  "adapterName": "@prisma/adapter-mariadb"\n}' +1ms
prisma:client Prisma Client call: +1ms
prisma:client prisma.sample.findMany() +0ms
prisma:client Generated request: +0ms
prisma:client {
  "modelName": "sample",
  "action": "findMany",
  "query": {
    "arguments": {},
    "selection": {
      "$composites": true,
      "$scalars": true
    }
  }
}
 +0ms
prisma:client:clientEngine sending request +0ms
prisma:driver-adapter:mariadb [js::getCapabilities] MySQL version: 9.0.1 from '[\n  [\n    "9.0.1"\n  ]\n]' +91ms
prisma:driver-adapter:mariadb [js::getCapabilities] Inferred capabilities: '{\n  "supportsRelationJoins": true\n}' +0ms
prisma:client:clientEngine query plan cache miss +77ms
prisma:client:clientEngine query plan created {
  "type": "dataMap",
  "args": {
    "expr": {
      "type": "query",
      "args": {
        "type": "templateSql",
        "fragments": [
          {
            "type": "stringChunk",
            "chunk": "SELECT `sample`.`id`, `sample`.`name_varchar_bin`, `sample`.`name_char_bin`, `sample`.`name_text_bin`, `sample`.`name_varchar`, `sample`.`name_char`, `sample`.`name_text`, `sample`.`data` FROM `sample` WHERE 1=1"
          }
        ],
        "args": [],
        "argTypes": [],
        "placeholderFormat": {
          "prefix": "?",
          "hasNumbering": false
        },
        "chunkable": true
      }
    },
    "structure": {
      "type": "object",
      "serializedName": null,
      "fields": {
        "id": {
          "type": "field",
          "dbName": "id",
          "fieldType": {
            "arity": "required",
            "type": "bigint"
          }
        },
        "name_varchar_bin": {
          "type": "field",
          "dbName": "name_varchar_bin",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "name_char_bin": {
          "type": "field",
          "dbName": "name_char_bin",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "name_text_bin": {
          "type": "field",
          "dbName": "name_text_bin",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "name_varchar": {
          "type": "field",
          "dbName": "name_varchar",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "name_char": {
          "type": "field",
          "dbName": "name_char",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "name_text": {
          "type": "field",
          "dbName": "name_text",
          "fieldType": {
            "arity": "required",
            "type": "string"
          }
        },
        "data": {
          "type": "field",
          "dbName": "data",
          "fieldType": {
            "arity": "required",
            "type": "bytes",
            "encoding": "array"
          }
        }
      },
      "skipNulls": false
    },
    "enums": {}
  }
} +11ms
prisma:driver-adapter:mariadb [js::query_raw] '{\n' +
  '  "sql": "SELECT `sample`.`id`, `sample`.`name_varchar_bin`, `sample`.`name_char_bin`, `sample`.`name_text_bin`, `sample`.`name_varchar`, `sample`.`name_char`, `sample`.`name_text`, `sample`.`data` FROM `sample` WHERE 1=1",\n' +
  '  "args": [],\n' +
  '  "argTypes": []\n' +
  '}' +43ms
prisma:client:clientEngine query plan executed +4ms
many:  [
  {
    id: 1n,
    name_varchar_bin: 'test',
    name_char_bin: 'test',
    name_text_bin: 'test',
    name_varchar: 'test',
    name_char: 'test',
    name_text: 'test',
    data: Uint8Array(1) [ 17 ]
  }
]
prisma:client prisma.$queryRaw(SELECT * FROM sample, []) +1ms
prisma:client Prisma Client call: +0ms
prisma:client prisma.$queryRaw({
  query: "SELECT * FROM sample",
  parameters: {
    values: "[]",
    __prismaRawParameters__: true
  }
}) +0ms
prisma:client Generated request: +0ms
prisma:client {
  "action": "queryRaw",
  "query": {
    "arguments": {
      "query": "SELECT * FROM sample",
      "parameters": "[]"
    },
    "selection": {}
  }
}
 +0ms
prisma:client:clientEngine sending request +0ms
prisma:client:clientEngine query plan created {
  "type": "query",
  "args": {
    "type": "rawSql",
    "sql": "SELECT * FROM sample",
    "args": [],
    "argTypes": []
  }
} +0ms
prisma:driver-adapter:mariadb [js::query_raw] '{\n  "sql": "SELECT * FROM sample",\n  "args": [],\n  "argTypes": []\n}' +5ms
prisma:client:clientEngine query plan executed +17ms
raw:  [
  {
    id: 1n,
    name_varchar_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_char_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_text_bin: Uint8Array(3) [ 181, 235, 45 ],
    name_varchar: 'test',
    name_char: 'test',
    name_text: 'test',
    data: Uint8Array(1) [ 17 ]
  }
]

Environment & Setup

  • OS: macOS
  • Database: MySQL
  • Node.js version: v24.13.0

Prisma Version

prisma               : 7.4.1
@prisma/client       : 7.4.1
Operating System     : darwin
Architecture         : arm64
Node.js              : v24.13.0
TypeScript           : unknown
Query Compiler       : enabled
PSL                  : @prisma/prisma-schema-wasm 7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3
Schema Engine        : schema-engine-cli 55ae170b1ced7fc6ed07a15f110549408c501bb3 (at node_modules/.pnpm/@prisma+engines@7.4.1/node_modules/@prisma/engines/schema-engine-darwin-arm64)
Default Engines Hash : 55ae170b1ced7fc6ed07a15f110549408c501bb3
Studio               : 0.13.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug/1-unconfirmedBug should have enough information for reproduction, but confirmation has not happened yet.kind/bugA reported bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions