Skip to content

Commit

Permalink
optimize (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
shiyuhang0 authored Sep 11, 2023
1 parent 95865b9 commit f8b39eb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 122 deletions.
172 changes: 58 additions & 114 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<host>',
username: '<user>',
password: '<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: '<host>',
username: '<user>',
password: '<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: '<host>',
username: '<user>',
password: '<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')
Expand All @@ -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: '<host>',
username: '<user>',
password: '<password>',
database: '<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 |
Expand All @@ -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).
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface Config {
database?: string
host?: string
fetch?: (input: string, init?: Req) => Promise<Res>
arrayMode?: boolean
fullResult?: boolean
}

export interface ExecuteOptions {
Expand Down
17 changes: 9 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Types>(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<Types>(typeByName, {})
return {
statement: sql,
types,
Expand Down

0 comments on commit f8b39eb

Please sign in to comment.