diff --git a/README.md b/README.md index 6486feb..72d81aa 100644 --- a/README.md +++ b/README.md @@ -2,57 +2,37 @@ This driver is for serverless and edge compute platforms that require HTTP external connections, such as Vercel Edge Functions or Cloudflare Workers. - -## Installation - -``` -npm install @tidbcloud/serverless -``` - ## Usage -```ts -import { connect } from '@tidbcloud/serverless' +**Install** -const config = { - host: '', - username: '', - password: '' -} +You can install the driver with npm: -const conn = connect(config) -const results = await conn.execute('select 1 from test') +```bash +npm install @tidbcloud/serverless ``` -Query with placeholder: +**Query** + +To query from TiDB serverless, you need to create a connection first. Then you can use the connection to execute raw SQL queries. For example: ```ts import { connect } from '@tidbcloud/serverless' -const config = { - host: '', - username: '', - password: '' -} - -const conn = connect(config) -const results = await conn.execute('select 1 from test where id = ?', [1]) -const results2 = await conn.execute('select 1 from test where id = :id', {id:1}) +const conn = connect({url: 'mysql://username:password@host/database'}) +const results = await conn.execute('select * from test where id = ?',[1]) ``` +**Transaction (Experimental)** -Use the `transaction` function to safely perform database transactions: +You can also perform interactive transactions with the TiDB serverless driver. For example: ```ts import { connect } from '@tidbcloud/serverless' -const config = { - host: '', - username: '', - password: '' -} -const conn = connect(config) +const conn = connect({url: 'mysql://username:password@host/database'}) const tx = await conn.begin() + try { await tx.execute('insert into test values (1)') await tx.execute('select * from test') @@ -63,52 +43,63 @@ try { } ``` -## Configuration +**Edge example** -The following configurations are supported in connection level: +The serverless driver is suitable for the edge environments. See how to use it with Vercel Edge Functions: -| name | type | default | comment | -|----------|----------|--------------|------------------------------------------------------------------| -| username | string | / | Username of TiDB Severless | -| password | string | / | Password of TiDB Severless | -| host | string | / | Host of TiDB Severless | -| database | string | test | Database of TiDB Severless | -| url | string | / | A single url format as `mysql://username:password@host/database` | -| fetch | function | global fetch | Custom fetch function | +``` +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { connect } from '@tidbcloud/serverless' +export const runtime = 'edge' -### Database URL +export async function GET(request: NextRequest) { + const conn = connect({url: process.env.DATABASE_URL}) + const result = await conn.execute('show tables') + return NextResponse.json({result}); +} +``` -A single database URL value can be used to configure the `host`, `username`, `password` and `database` values: +See [TiDB serverless driver](https://docs.pingcap.com/tidbcloud/serverless-driver#edge-examples) documentation to learn more examples. -```ts -const conn = connect({url: process.env['DATABASE_URL'] || 'mysql://username:password@host/database'}) -``` +## Configuration -Database can be skipped to use the default one: +The following configurations are supported in connection level: -```ts -const conn = connect({url: process.env['DATABASE_URL'] || 'mysql://username:password@host'}) -```` +| name | type | default | comment | +|------------|-----------|--------------|------------------------------------------------------------------| +| username | string | / | Username of TiDB Severless | +| password | string | / | Password of TiDB Severless | +| host | string | / | Host of TiDB Severless | +| database | string | test | Database of TiDB Severless | +| url | string | / | A single url format as `mysql://username:password@host/database` | +| fetch | function | global fetch | Custom fetch function | +| arrayMode | bool | false | whether to return results as arrays instead of objects | +| fullResult | bool | false | whether to return full result object instead of just rows | -### Custom fetch function +### Database URL -You can custom fetch function instead of using the global one. It's useful when you run in the environment without a built-in global `fetch` function. For example, an older version of Node.js. +A single database URL value can be used to configure the `host`, `username`, `password` and `database` values. The following codes are equivalent: ```ts -import { connect } from '@tidbcloud/serverless' -import { fetch } from 'undici' - const config = { - fetch, - url: process.env['DATABASE_URL'] || 'mysql://username:password@host/database' + host: '', + username: '', + password: '', + database: '' } const conn = connect(config) -const results = await conn.execute('select 1 from test') +``` + +```ts +const conn = connect({url: process.env['DATABASE_URL'] || 'mysql://username:password@host/database'}) ``` ## Options +> Note: SQL level options priority is higher than connection level configurations. + The following options are supported in SQL level: | option | type | default | comment | @@ -128,57 +119,10 @@ const conn = connect(config) const results = await conn.execute('select 1 from test',null,{arrayMode:true,fullResult:true}) ``` -## Features - -### Supported SQL - -The following SQL statements are supported: `Select`, `Show`, `Explain`, `Use`, `Insert`, `Update`, `Delete`, `Begin`, `Commit`, `Rollback`. - -And most of the DDL are supported. - -### Data Type Mapping - -The type mapping between TiDB and Javascript is as follows: - -| TiDB Type | Javascript Type | -|-------------------|-----------------| -| TINYINT | number | -| UNSIGNED TINYINT | number | -| BOOL | number | -| SMALLINT | number | -| UNSIGNED SMALLINT | number | -| MEDIUMINT | number | -| INT | number | -| UNSIGNED INT | number | -| YEAR | number | -| FLOAT | number | -| DOUBLE | number | -| BIGINT | string | -| UNSIGNED BIGINT | string | -| DECIMAL | string | -| CHAR | string | -| VARCHAR | string | -| BINARY | string | -| VARBINARY | string | -| TINYTEXT | string | -| TEXT | string | -| MEDIUMTEXT | string | -| LONGTEXT | string | -| TINYBLOB | string | -| BLOB | string | -| MEDIUMBLOB | string | -| LONGBLOB | string | -| DATE | string | -| TIME | string | -| DATETIME | string | -| TIMESTAMP | string | -| ENUM | string | -| SET | string | -| BIT | string | -| JSON | object | -| NULL | null | -| Others | string | - -# Limitations - -- Up to 10,000 rows can be fetched in a single query. +## Documentation + +See [TiDB serverless driver](https://docs.pingcap.com/tidbcloud/serverless-driver) documentation to learn more. + +## License + +Apache 2.0, see [LICENSE](./LICENSE). diff --git a/src/config.ts b/src/config.ts index 2d87b4f..3b492c3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -20,6 +20,8 @@ export interface Config { database?: string host?: string fetch?: (input: string, init?: Req) => Promise + arrayMode?: boolean + fullResult?: boolean } export interface ExecuteOptions { diff --git a/src/index.ts b/src/index.ts index 9c6a14a..53a428b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,17 +103,18 @@ export class Connection { if (this.session === null) { throw new DatabaseError('empty session, please try again', 500, null) } - const rowsAffected = resp?.rowsAffected ?? 0 - const lastInsertId = resp?.lastInsertID ?? null - const fields = resp?.types ?? [] - const typeByName = (acc, { name, type }) => ({ ...acc, [name]: type }) - const types = fields.reduce(typeByName, {}) + const arrayMode = options.arrayMode ?? this.config.arrayMode ?? false + const fullResult = options.fullResult ?? this.config.arrayMode ?? false - const datas = resp?.rows ?? [] - const rows = resp ? parse(fields, datas, cast, options.arrayMode || false) : [] + const fields = resp?.types ?? [] + const rows = resp ? parse(fields, resp?.rows ?? [], cast, arrayMode) : [] - if (options.fullResult) { + if (fullResult) { + const rowsAffected = resp?.rowsAffected ?? 0 + const lastInsertId = resp?.lastInsertID ?? null + const typeByName = (acc, { name, type }) => ({ ...acc, [name]: type }) + const types = fields.reduce(typeByName, {}) return { statement: sql, types,