diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23da817..0bc0c23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,6 @@ jobs: - name: lint run: pnpm lint - name: build - run: pnpm build \ No newline at end of file + run: pnpm build + - name: fmt + run: pnpm fmt-check \ No newline at end of file diff --git a/package.json b/package.json index 4b56391..17e53db 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsup ./src/index.ts --format cjs,esm --dts", - "lint": "tsc -p ./tsconfig.build.json" + "lint": "tsc -p ./tsconfig.build.json", + "fmt": "prettier --write ./src", + "fmt-check": "prettier --check ./src" }, "files": [ "dist", @@ -39,7 +41,8 @@ "@types/node": "^20.5.1", "tsup": "^7.2.0", "tsx": "^3.12.7", - "typescript": "^5.1.6" + "typescript": "^5.1.6", + "prettier": "^2.7.1" }, "peerDependencies": { "@tidbcloud/serverless": ">= 0.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de42d55..a95d699 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,9 @@ devDependencies: '@types/node': specifier: ^20.5.1 version: 20.5.1 + prettier: + specifier: ^2.7.1 + version: 2.7.1 tsup: specifier: ^7.2.0 version: 7.2.0(typescript@5.1.6) @@ -738,6 +741,12 @@ packages: yaml: 2.3.2 dev: true + /prettier@2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} diff --git a/src/conversion.ts b/src/conversion.ts index bd6debb..508cf3b 100644 --- a/src/conversion.ts +++ b/src/conversion.ts @@ -1,39 +1,39 @@ -import { ColumnTypeEnum, type ColumnType } from '@prisma/driver-adapter-utils' +import { ColumnTypeEnum, type ColumnType } from "@prisma/driver-adapter-utils"; -export type TiDBCloudColumnType - = 'NULL' - | 'TINYINT' - | 'UNSIGNED TINYINT' - | 'SMALLINT' - | 'UNSIGNED SMALLINT' - | 'MEDIUMINT' - | 'UNSIGNED MEDIUMINT' - | 'INT' - | 'UNSIGNED INT' - | 'YEAR' - | 'FLOAT' - | 'DOUBLE' - | 'BIGINT' - | 'UNSIGNED BIGINT' - | 'DECIMAL' - | 'CHAR' - | 'VARCHAR' - | 'BINARY' - | 'VARBINARY' - | 'TINYTEXT' - | 'TEXT' - | 'MEDIUMTEXT' - | 'LONGTEXT' - | 'TINYBLOB' - | 'BLOB' - | 'MEDIUMBLOB' - | 'LONGBLOB' - | 'DATE' - | 'TIME' - | 'DATETIME' - | 'TIMESTAMP' - | 'JSON' - | 'BIT' +export type TiDBCloudColumnType = + | "NULL" + | "TINYINT" + | "UNSIGNED TINYINT" + | "SMALLINT" + | "UNSIGNED SMALLINT" + | "MEDIUMINT" + | "UNSIGNED MEDIUMINT" + | "INT" + | "UNSIGNED INT" + | "YEAR" + | "FLOAT" + | "DOUBLE" + | "BIGINT" + | "UNSIGNED BIGINT" + | "DECIMAL" + | "CHAR" + | "VARCHAR" + | "BINARY" + | "VARBINARY" + | "TINYTEXT" + | "TEXT" + | "MEDIUMTEXT" + | "LONGTEXT" + | "TINYBLOB" + | "BLOB" + | "MEDIUMBLOB" + | "LONGBLOB" + | "DATE" + | "TIME" + | "DATETIME" + | "TIMESTAMP" + | "JSON" + | "BIT"; /** * This is a simplification of quaint's value inference logic. Take a look at quaint's conversion.rs @@ -42,72 +42,72 @@ export type TiDBCloudColumnType */ export function fieldToColumnType(field: TiDBCloudColumnType): ColumnType { switch (field) { - case 'TINYINT': - case 'UNSIGNED TINYINT': - case 'SMALLINT': - case 'UNSIGNED SMALLINT': - case 'MEDIUMINT': - case 'UNSIGNED MEDIUMINT': - case 'INT': - case 'YEAR': - return ColumnTypeEnum.Int32 - case 'UNSIGNED INT': - case 'BIGINT': - case 'UNSIGNED BIGINT': - return ColumnTypeEnum.Int64 - case 'FLOAT': - return ColumnTypeEnum.Float - case 'DOUBLE': - return ColumnTypeEnum.Double - case 'TIMESTAMP': - case 'DATETIME': - return ColumnTypeEnum.DateTime - case 'DATE': - return ColumnTypeEnum.Date - case 'TIME': - return ColumnTypeEnum.Time - case 'DECIMAL': - return ColumnTypeEnum.Numeric - case 'CHAR': - case 'TINYTEXT': - case 'TEXT': - case 'MEDIUMTEXT': - case 'LONGTEXT': - case 'VARCHAR': - return ColumnTypeEnum.Text - case 'JSON': - return ColumnTypeEnum.Json - case 'TINYBLOB': - case 'BLOB': - case 'MEDIUMBLOB': - case 'LONGBLOB': - case 'BINARY': - case 'VARBINARY': - case 'BIT': - return ColumnTypeEnum.Bytes - case 'NULL': + case "TINYINT": + case "UNSIGNED TINYINT": + case "SMALLINT": + case "UNSIGNED SMALLINT": + case "MEDIUMINT": + case "UNSIGNED MEDIUMINT": + case "INT": + case "YEAR": + return ColumnTypeEnum.Int32; + case "UNSIGNED INT": + case "BIGINT": + case "UNSIGNED BIGINT": + return ColumnTypeEnum.Int64; + case "FLOAT": + return ColumnTypeEnum.Float; + case "DOUBLE": + return ColumnTypeEnum.Double; + case "TIMESTAMP": + case "DATETIME": + return ColumnTypeEnum.DateTime; + case "DATE": + return ColumnTypeEnum.Date; + case "TIME": + return ColumnTypeEnum.Time; + case "DECIMAL": + return ColumnTypeEnum.Numeric; + case "CHAR": + case "TINYTEXT": + case "TEXT": + case "MEDIUMTEXT": + case "LONGTEXT": + case "VARCHAR": + return ColumnTypeEnum.Text; + case "JSON": + return ColumnTypeEnum.Json; + case "TINYBLOB": + case "BLOB": + case "MEDIUMBLOB": + case "LONGBLOB": + case "BINARY": + case "VARBINARY": + case "BIT": + return ColumnTypeEnum.Bytes; + case "NULL": // Fall back to Int32 for consistency with quaint. - return ColumnTypeEnum.Int32 + return ColumnTypeEnum.Int32; default: - throw new Error(`Unsupported column type: ${field}`) + throw new Error(`Unsupported column type: ${field}`); } } // define the decoder because TiDB Cloud serverless driver returns Uint8Array for these type export const customDecoder = { - "BINARY": (value: string) => Array.from(hexToUint8Array(value)), - "VARBINARY": (value: string) => Array.from(hexToUint8Array(value)), - "BLOB": (value: string) => Array.from(hexToUint8Array(value)), - "LONGBLOB": (value: string) => Array.from(hexToUint8Array(value)), - "TINYINT": (value: string) => Array.from(hexToUint8Array(value)), - "MEDIUMBLOB": (value: string) => Array.from(hexToUint8Array(value)), - "BIT": (value: string) => Array.from(hexToUint8Array(value)), -} + BINARY: (value: string) => Array.from(hexToUint8Array(value)), + VARBINARY: (value: string) => Array.from(hexToUint8Array(value)), + BLOB: (value: string) => Array.from(hexToUint8Array(value)), + LONGBLOB: (value: string) => Array.from(hexToUint8Array(value)), + TINYINT: (value: string) => Array.from(hexToUint8Array(value)), + MEDIUMBLOB: (value: string) => Array.from(hexToUint8Array(value)), + BIT: (value: string) => Array.from(hexToUint8Array(value)), +}; function hexToUint8Array(hexString: string): Uint8Array { - const uint8Array = new Uint8Array(hexString.length / 2) + const uint8Array = new Uint8Array(hexString.length / 2); for (let i = 0; i < hexString.length; i += 2) { - uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16) + uint8Array[i / 2] = parseInt(hexString.substring(i, i + 2), 16); } - return uint8Array -} \ No newline at end of file + return uint8Array; +} diff --git a/src/index.ts b/src/index.ts index 7257061..ed6dc38 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export { PrismaTiDBCloud } from './tidbcloud' \ No newline at end of file +export { PrismaTiDBCloud } from "./tidbcloud"; diff --git a/src/tidbcloud.ts b/src/tidbcloud.ts index 694f3da..40af289 100644 --- a/src/tidbcloud.ts +++ b/src/tidbcloud.ts @@ -1,5 +1,5 @@ -import type TiDBCloud from '@tidbcloud/serverless' -import { Debug, ok } from '@prisma/driver-adapter-utils' +import type TiDBCloud from "@tidbcloud/serverless"; +import { Debug, ok } from "@prisma/driver-adapter-utils"; import type { ConnectionInfo, DriverAdapter, @@ -9,50 +9,58 @@ import type { Transaction, Result, TransactionOptions, -} from '@prisma/driver-adapter-utils' -import {type TiDBCloudColumnType,fieldToColumnType,customDecoder} from './conversion' +} from "@prisma/driver-adapter-utils"; +import { + type TiDBCloudColumnType, + fieldToColumnType, + customDecoder, +} from "./conversion"; -const debug = Debug('prisma:driver-adapter:tidbcloud') +const debug = Debug("prisma:driver-adapter:tidbcloud"); -const defaultDatabase = 'test' +const defaultDatabase = "test"; class RollbackError extends Error { constructor() { - super('ROLLBACK') - this.name = 'RollbackError' + super("ROLLBACK"); + this.name = "RollbackError"; if (Error.captureStackTrace) { - Error.captureStackTrace(this, RollbackError) + Error.captureStackTrace(this, RollbackError); } } } -class TiDBCloudQueryable implements Queryable { - readonly provider = 'mysql' +class TiDBCloudQueryable + implements Queryable +{ + readonly provider = "mysql"; constructor(protected client: ClientT) {} /** * Execute a query given as SQL, interpolating the given parameters. */ async queryRaw(query: Query): Promise> { - const tag = '[js::query_raw]' - debug(`${tag} %O`, query) + const tag = "[js::query_raw]"; + debug(`${tag} %O`, query); - const result = await this.performIO(query) - const fields = result.types as TiDBCloud.Types - const rows = result.rows as TiDBCloud.Row[] - const lastInsertId = result.lastInsertId?.toString() + const result = await this.performIO(query); + const fields = result.types as TiDBCloud.Types; + const rows = result.rows as TiDBCloud.Row[]; + const lastInsertId = result.lastInsertId?.toString(); - const columnNames = Object.keys(fields) as string[] - const columnRawTypes = Object.values(fields) as string[] + const columnNames = Object.keys(fields) as string[]; + const columnRawTypes = Object.values(fields) as string[]; const resultSet: ResultSet = { columnNames, - columnTypes: columnRawTypes.map((field) => fieldToColumnType(field as TiDBCloudColumnType)), - rows: rows as ResultSet['rows'], - lastInsertId - } - return ok(resultSet) + columnTypes: columnRawTypes.map((field) => + fieldToColumnType(field as TiDBCloudColumnType) + ), + rows: rows as ResultSet["rows"], + lastInsertId, + }; + return ok(resultSet); } /** @@ -61,12 +69,12 @@ class TiDBCloudQueryable im * Note: Queryable expects a u64, but napi.rs only supports u32. */ async executeRaw(query: Query): Promise> { - const tag = '[js::execute_raw]' - debug(`${tag} %O`, query) + const tag = "[js::execute_raw]"; + debug(`${tag} %O`, query); - const result = await this.performIO(query) - const rowsAffected = result.rowsAffected as number - return ok(rowsAffected) + const result = await this.performIO(query); + const rowsAffected = result.rowsAffected as number; + return ok(rowsAffected); } /** @@ -75,75 +83,82 @@ class TiDBCloudQueryable im * marked as unhealthy. */ private async performIO(query: Query) { - const { sql, args: values } = query + const { sql, args: values } = query; try { - const result = await this.client.execute(sql, values, {arrayMode: true, fullResult: true, decoders:customDecoder}) - return result as TiDBCloud.FullResult + const result = await this.client.execute(sql, values, { + arrayMode: true, + fullResult: true, + decoders: customDecoder, + }); + return result as TiDBCloud.FullResult; } catch (e) { - const error = e as Error - debug('Error in performIO: %O', error) - throw error + const error = e as Error; + debug("Error in performIO: %O", error); + throw error; } } } -class TiDBCloudTransaction extends TiDBCloudQueryable implements Transaction { - finished = false +class TiDBCloudTransaction + extends TiDBCloudQueryable + implements Transaction +{ + finished = false; - constructor( - tx: TiDBCloud.Tx, - readonly options: TransactionOptions, - ) { - super(tx) + constructor(tx: TiDBCloud.Tx, readonly options: TransactionOptions) { + super(tx); } async commit(): Promise> { - debug(`[js::commit]`) + debug(`[js::commit]`); - this.finished = true - await this.client.commit() - return Promise.resolve(ok(undefined)) + this.finished = true; + await this.client.commit(); + return Promise.resolve(ok(undefined)); } async rollback(): Promise> { - debug(`[js::rollback]`) + debug(`[js::rollback]`); - this.finished = true - await this.client.rollback() - return Promise.resolve(ok(undefined)) + this.finished = true; + await this.client.rollback(); + return Promise.resolve(ok(undefined)); } dispose(): Result { if (!this.finished) { - this.rollback().catch(console.error) + this.rollback().catch(console.error); } - return ok(undefined) + return ok(undefined); } } -export class PrismaTiDBCloud extends TiDBCloudQueryable implements DriverAdapter { +export class PrismaTiDBCloud + extends TiDBCloudQueryable + implements DriverAdapter +{ constructor(client: TiDBCloud.Connection) { - super(client) + super(client); } getConnectionInfo(): Result { - const config = this.client.getConfig() - const dbName = config.database? config.database : defaultDatabase + const config = this.client.getConfig(); + const dbName = config.database ? config.database : defaultDatabase; return ok({ schemaName: dbName, - }) + }); } async startTransaction() { const options: TransactionOptions = { usePhantomQuery: true, - } + }; - const tag = '[js::startTransaction]' - debug(`${tag} options: %O`, options) + const tag = "[js::startTransaction]"; + debug(`${tag} options: %O`, options); - const tx = await this.client.begin() - return ok(new TiDBCloudTransaction(tx, options)) + const tx = await this.client.begin(); + return ok(new TiDBCloudTransaction(tx, options)); } }