Skip to content

Commit 7ec585f

Browse files
Merge pull request #166 from drizzle-team/bug-158
Fix .get mapping for sqlite
2 parents 21b892d + 7c4eeaf commit 7ec585f

File tree

15 files changed

+913
-76
lines changed

15 files changed

+913
-76
lines changed

changelogs/drizzle-orm/0.17.7.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- Fix [#158](https://github.com/drizzle-team/drizzle-orm/issues/158) issue. Method `.returning()` was working incorrectly with `.get()` method in sqlite dialect
2+
- Fix SQLite Proxy driver mapping bug
3+
- Add test cases for SQLite Proxy driver
4+
- Add additional example for SQLite Proxy Server setup to handle `.get()` as well

drizzle-orm/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "drizzle-orm",
3-
"version": "0.17.6",
3+
"version": "0.17.7",
44
"description": "Drizzle ORM package for SQL databases",
55
"scripts": {
66
"build": "tsc && resolve-tspaths && cp ../README.md package.json dist/",

drizzle-orm/src/better-sqlite3/session.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,14 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>
7474
get(placeholderValues?: Record<string, unknown>): T['get'] {
7575
const params = fillPlaceholders(this.params, placeholderValues ?? {});
7676
this.logger.logQuery(this.queryString, params);
77-
const value = this.stmt.get(...params);
7877

7978
const { fields } = this;
8079
if (!fields) {
81-
return value;
80+
return this.stmt.get(...params);
8281
}
8382

83+
const value = this.stmt.raw().get(...params);
84+
8485
return mapResultRow(fields, value);
8586
}
8687

drizzle-orm/src/sqlite-proxy/driver.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ export interface SqliteRemoteResult<T = unknown> {
1313

1414
export type SqliteRemoteDatabase = BaseSQLiteDatabase<'async', SqliteRemoteResult>;
1515

16-
export type RemoteCallback = (
16+
export type AsyncRemoteCallback = (
1717
sql: string,
1818
params: any[],
19-
method: 'run' | 'all' | 'values',
20-
) => Promise<{ rows: any[][] }>;
19+
method: 'run' | 'all' | 'values' | 'get',
20+
) => Promise<{ rows: any[] }>;
21+
22+
export type RemoteCallback = AsyncRemoteCallback;
2123

2224
export function drizzle(callback: RemoteCallback, config: DrizzleConfig = {}): SqliteRemoteDatabase {
2325
const dialect = new SQLiteAsyncDialect();

drizzle-orm/src/sqlite-proxy/session.ts

+26-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ export class SQLiteRemoteSession extends SQLiteSession<'async', SqliteRemoteResu
3434
// return this.client(this.queryString, params).then(({ rows }) => rows!)
3535
}
3636

37-
prepareQuery<T extends Omit<PreparedQueryConfig, 'run'>>(query: Query, fields?: SelectFieldsOrdered): PreparedQuery<T> {
37+
prepareQuery<T extends Omit<PreparedQueryConfig, 'run'>>(
38+
query: Query,
39+
fields?: SelectFieldsOrdered,
40+
): PreparedQuery<T> {
3841
return new PreparedQuery(this.client, query.sql, query.params, this.logger, fields);
3942
}
4043
}
@@ -60,17 +63,35 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>
6063

6164
async all(placeholderValues?: Record<string, unknown>): Promise<T['all']> {
6265
const { fields } = this;
63-
if (fields) {
64-
return this.values(placeholderValues).then((values) => values.map((row) => mapResultRow(fields, row)));
65-
}
6666

6767
const params = fillPlaceholders(this.params, placeholderValues ?? {});
6868
this.logger.logQuery(this.queryString, params);
69+
70+
const clientResult = this.client(this.queryString, params, 'all');
71+
72+
if (fields) {
73+
return clientResult.then((values) => values.rows.map((row) => mapResultRow(fields, row)));
74+
}
75+
6976
return this.client(this.queryString, params, 'all').then(({ rows }) => rows!);
7077
}
7178

7279
async get(placeholderValues?: Record<string, unknown>): Promise<T['get']> {
73-
return await this.all(placeholderValues).then((rows) => rows[0]);
80+
const { fields } = this;
81+
82+
const params = fillPlaceholders(this.params, placeholderValues ?? {});
83+
this.logger.logQuery(this.queryString, params);
84+
85+
const clientResult = await this.client(this.queryString, params, 'get');
86+
87+
if (fields) {
88+
if (typeof clientResult.rows === 'undefined') {
89+
return mapResultRow(fields, []);
90+
}
91+
return mapResultRow(fields, clientResult.rows);
92+
}
93+
94+
return clientResult.rows;
7495
}
7596

7697
async values<T extends any[] = unknown[]>(placeholderValues?: Record<string, unknown>): Promise<T[]> {

examples/sqlite-proxy/README.md

+22-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Subscribe to our updates on [Twitter](https://twitter.com/DrizzleOrm) and [Disco
99
SQLite Proxy driver will do all the work except of 2 things, that you will be responsible for:
1010

1111
1. Calls to database, http servers or any other way to communicate with database
12-
2. Mapping data from database to `{rows: string[][], ...additional db response params}` format. Only `row` field is required
12+
2. Mapping data from database to `{rows: any[], ...additional db response params}` format. Only `rows` field is required. Rows should be a row array from database
1313

1414
</br>
1515
This project has simple example of defining http proxy server, that will proxy all calls from drizzle orm to database and back. This example could perfectly fit for serverless applications
@@ -32,6 +32,8 @@ This project has simple example of defining http proxy server, that will proxy a
3232

3333
> **Warning**:
3434
> You will be responsible for proper error handling in this part. Drizzle always waits for `{rows: string[][]}` so if any error was on http call(or any other call) - be sure, that you return at least empty array back
35+
>
36+
> For `get` method you should return `{rows: string[]}`
3537
3638
</br>
3739

@@ -59,7 +61,7 @@ We have 3 params, that will be sent to server. It's your decision which of them
5961

6062
1. `sql` - SQL query (`SELECT * FROM users WHERE id = ?`)
6163
2. `params` - params, that should be sent on database call (For query above it could be: `[1]`)
62-
3. `method` - Method, that was executed (`run` | `all` | `values`). Hint for proxy server on which sqlite method to invoke
64+
3. `method` - Method, that was executed (`run` | `all` | `values` | `get`). Hint for proxy server on which sqlite method to invoke
6365

6466
### Migrations using SQLite Proxy
6567

@@ -78,15 +80,14 @@ In current SQLite Proxy version - drizzle don't handle transactions for migratio
7880
import axios from 'axios';
7981
import { migrate } from 'drizzle-orm/sqlite-proxy/migrator';
8082

81-
8283
await migrate(db, async (queries) => {
83-
try {
84-
await axios.post('http://localhost:3000/migrate', { queries });
85-
} catch (e) {
86-
console.log(e)
87-
throw Error('Proxy server cannot run migrations')
88-
}
89-
}, { migrationsFolder: 'drizzle' });
84+
try {
85+
await axios.post('http://localhost:3000/migrate', { queries });
86+
} catch (e) {
87+
console.log(e);
88+
throw Error('Proxy server cannot run migrations');
89+
}
90+
}, { migrationsFolder: 'drizzle' });
9091
```
9192

9293
1. `queries` - array of sql statements, that should be run on migration
@@ -116,14 +117,21 @@ app.post('/query', (req, res) => {
116117
const result = db.prepare(sqlBody).run(params);
117118
res.send(result);
118119
} catch (e: any) {
119-
res.status(500).json({ error: e.message });
120+
res.status(500).json({ error: e.message });
120121
}
121122
} else if (method === 'all' || method === 'values') {
122-
try {
123+
try {
123124
const rows = db.prepare(sqlBody).raw().all(params);
124-
res.send(rows);
125+
res.send(rows);
126+
} catch (e: any) {
127+
res.status(500).json({ error: e.message });
128+
}
129+
} else if (method === 'get') {
130+
try {
131+
const row = this.db.prepare(sql).raw().get(params);
132+
return { data: row };
125133
} catch (e: any) {
126-
res.status(500).json({ error: e.message });
134+
return { error: e.message };
127135
}
128136
} else {
129137
res.status(500).json({ error: 'Unkown method value' });

examples/sqlite-proxy/drizzle/20230202162455/migration.sql

-5
This file was deleted.

examples/sqlite-proxy/drizzle/20230202162455/snapshot.json

-36
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE cities (
2+
`id` integer PRIMARY KEY NOT NULL,
3+
`name` text NOT NULL
4+
);
5+
6+
CREATE TABLE users (
7+
`id` integer PRIMARY KEY NOT NULL,
8+
`name` text NOT NULL,
9+
`email` text NOT NULL,
10+
`city_id` integer
11+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"version": "4",
3+
"dialect": "sqlite",
4+
"id": "71c97670-a6f8-42e0-9a15-4991f89e2134",
5+
"prevId": "00000000-0000-0000-0000-000000000000",
6+
"tables": {
7+
"cities": {
8+
"name": "cities",
9+
"columns": {
10+
"id": {
11+
"name": "id",
12+
"type": "integer",
13+
"primaryKey": true,
14+
"notNull": true,
15+
"autoincrement": false
16+
},
17+
"name": {
18+
"name": "name",
19+
"type": "text",
20+
"primaryKey": false,
21+
"notNull": true
22+
}
23+
},
24+
"indexes": {},
25+
"foreignKeys": {},
26+
"compositePrimaryKeys": {}
27+
},
28+
"users": {
29+
"name": "users",
30+
"columns": {
31+
"id": {
32+
"name": "id",
33+
"type": "integer",
34+
"primaryKey": true,
35+
"notNull": true,
36+
"autoincrement": false
37+
},
38+
"name": {
39+
"name": "name",
40+
"type": "text",
41+
"primaryKey": false,
42+
"notNull": true
43+
},
44+
"email": {
45+
"name": "email",
46+
"type": "text",
47+
"primaryKey": false,
48+
"notNull": true
49+
},
50+
"city_id": {
51+
"name": "city_id",
52+
"type": "integer",
53+
"primaryKey": false,
54+
"notNull": false,
55+
"autoincrement": false
56+
}
57+
},
58+
"indexes": {},
59+
"foreignKeys": {},
60+
"compositePrimaryKeys": {}
61+
}
62+
},
63+
"enums": {}
64+
}

examples/sqlite-proxy/src/index.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import axios from 'axios';
2+
import { eq } from 'drizzle-orm/expressions';
23
import { drizzle } from 'drizzle-orm/sqlite-proxy';
34
import { migrate } from 'drizzle-orm/sqlite-proxy/migrator';
4-
import { users } from './schema';
5+
import { cities, users } from './schema';
56

67
async function main() {
78
const db = drizzle(async (sql, params, method) => {
@@ -10,7 +11,7 @@ async function main() {
1011

1112
return { rows: rows.data };
1213
} catch (e: any) {
13-
console.error('Error from sqlite proxy server: ', e.response.data)
14+
console.error('Error from sqlite proxy server: ', e.response.data);
1415
return { rows: [] };
1516
}
1617
});
@@ -19,16 +20,19 @@ async function main() {
1920
try {
2021
await axios.post('http://localhost:3000/migrate', { queries });
2122
} catch (e) {
22-
console.log(e)
23-
throw Error('Proxy server cannot run migrations')
23+
console.log(e);
24+
throw Error('Proxy server cannot run migrations');
2425
}
2526
}, { migrationsFolder: 'drizzle' });
2627

27-
const insertResult = await db.insert(users).values({ id: 1, name: 'name', email: 'email' }).run();
28-
console.log(insertResult)
28+
const insertedCity = await db.insert(cities).values({ id: 1, name: 'name' }).returning().get();
29+
console.log('insertedCity: ', insertedCity);
2930

30-
const usersResponse = await db.select(users).all();
31-
console.log(usersResponse);
31+
const insertedUser = await db.insert(users).values({ id: 1, name: 'name', email: 'email', cityId: 1 }).run();
32+
console.log('insertedUser: ', insertedUser);
33+
34+
const usersToCityResponse = await db.select(users).leftJoin(cities, eq(users.cityId, cities.id)).get();
35+
console.log('usersToCityResponse: ', usersToCityResponse);
3236
}
3337

34-
main();
38+
main();

examples/sqlite-proxy/src/schema.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
22

3-
export const users = sqliteTable('users12', {
3+
export const users = sqliteTable('users', {
44
id: int('id').primaryKey(),
55
name: text('name').notNull(),
66
email: text('email').notNull(),
7+
cityId: int('city_id'),
8+
});
9+
10+
export const cities = sqliteTable('cities', {
11+
id: int('id').primaryKey(),
12+
name: text('name').notNull(),
713
});

examples/sqlite-proxy/src/server.ts

+7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ app.post('/query', (req, res) => {
2424
} catch (e: any) {
2525
res.status(500).json({ error: e.message });
2626
}
27+
}else if (method === 'get') {
28+
try {
29+
const row = db.prepare(sqlBody).raw().get(params);
30+
return { data: row };
31+
} catch (e: any) {
32+
return { error: e.message };
33+
}
2734
} else {
2835
res.status(500).json({ error: 'Unkown method value' });
2936
}

0 commit comments

Comments
 (0)