-
Notifications
You must be signed in to change notification settings - Fork 2.1k
$queryRaw returns Uint8Array instead of string for string data columns with binary collation #29237
Description
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
- 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'
);- 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();- 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