Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix .get mapping for sqlite #166

Merged
merged 1 commit into from
Feb 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelogs/drizzle-orm/0.17.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Fix [#158](https://github.com/drizzle-team/drizzle-orm/issues/158) issue. Method `.returning()` was working incorrectly with `.get()` method in sqlite dialect
- Fix SQLite Proxy driver mapping bug
- Add test cases for SQLite Proxy driver
- Add additional example for SQLite Proxy Server setup to handle `.get()` as well
2 changes: 1 addition & 1 deletion drizzle-orm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "drizzle-orm",
"version": "0.17.6",
"version": "0.17.7",
"description": "Drizzle ORM package for SQL databases",
"scripts": {
"build": "tsc && resolve-tspaths && cp ../README.md package.json dist/",
Expand Down
5 changes: 3 additions & 2 deletions drizzle-orm/src/better-sqlite3/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ export class PreparedQuery<T extends PreparedQueryConfig = PreparedQueryConfig>
get(placeholderValues?: Record<string, unknown>): T['get'] {
const params = fillPlaceholders(this.params, placeholderValues ?? {});
this.logger.logQuery(this.queryString, params);
const value = this.stmt.get(...params);

const { fields } = this;
if (!fields) {
return value;
return this.stmt.get(...params);
}

const value = this.stmt.raw().get(...params);

return mapResultRow(fields, value);
}

Expand Down
8 changes: 5 additions & 3 deletions drizzle-orm/src/sqlite-proxy/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ export interface SqliteRemoteResult<T = unknown> {

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

export type RemoteCallback = (
export type AsyncRemoteCallback = (
sql: string,
params: any[],
method: 'run' | 'all' | 'values',
) => Promise<{ rows: any[][] }>;
method: 'run' | 'all' | 'values' | 'get',
) => Promise<{ rows: any[] }>;

export type RemoteCallback = AsyncRemoteCallback;

export function drizzle(callback: RemoteCallback, config: DrizzleConfig = {}): SqliteRemoteDatabase {
const dialect = new SQLiteAsyncDialect();
Expand Down
31 changes: 26 additions & 5 deletions drizzle-orm/src/sqlite-proxy/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export class SQLiteRemoteSession extends SQLiteSession<'async', SqliteRemoteResu
// return this.client(this.queryString, params).then(({ rows }) => rows!)
}

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

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

const params = fillPlaceholders(this.params, placeholderValues ?? {});
this.logger.logQuery(this.queryString, params);

const clientResult = this.client(this.queryString, params, 'all');

if (fields) {
return clientResult.then((values) => values.rows.map((row) => mapResultRow(fields, row)));
}

return this.client(this.queryString, params, 'all').then(({ rows }) => rows!);
}

async get(placeholderValues?: Record<string, unknown>): Promise<T['get']> {
return await this.all(placeholderValues).then((rows) => rows[0]);
const { fields } = this;

const params = fillPlaceholders(this.params, placeholderValues ?? {});
this.logger.logQuery(this.queryString, params);

const clientResult = await this.client(this.queryString, params, 'get');

if (fields) {
if (typeof clientResult.rows === 'undefined') {
return mapResultRow(fields, []);
}
return mapResultRow(fields, clientResult.rows);
}

return clientResult.rows;
}

async values<T extends any[] = unknown[]>(placeholderValues?: Record<string, unknown>): Promise<T[]> {
Expand Down
36 changes: 22 additions & 14 deletions examples/sqlite-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Subscribe to our updates on [Twitter](https://twitter.com/DrizzleOrm) and [Disco
SQLite Proxy driver will do all the work except of 2 things, that you will be responsible for:

1. Calls to database, http servers or any other way to communicate with database
2. Mapping data from database to `{rows: string[][], ...additional db response params}` format. Only `row` field is required
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

</br>
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
Expand All @@ -32,6 +32,8 @@ This project has simple example of defining http proxy server, that will proxy a

> **Warning**:
> 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
>
> For `get` method you should return `{rows: string[]}`

</br>

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

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

### Migrations using SQLite Proxy

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


await migrate(db, async (queries) => {
try {
await axios.post('http://localhost:3000/migrate', { queries });
} catch (e) {
console.log(e)
throw Error('Proxy server cannot run migrations')
}
}, { migrationsFolder: 'drizzle' });
try {
await axios.post('http://localhost:3000/migrate', { queries });
} catch (e) {
console.log(e);
throw Error('Proxy server cannot run migrations');
}
}, { migrationsFolder: 'drizzle' });
```

1. `queries` - array of sql statements, that should be run on migration
Expand Down Expand Up @@ -116,14 +117,21 @@ app.post('/query', (req, res) => {
const result = db.prepare(sqlBody).run(params);
res.send(result);
} catch (e: any) {
res.status(500).json({ error: e.message });
res.status(500).json({ error: e.message });
}
} else if (method === 'all' || method === 'values') {
try {
try {
const rows = db.prepare(sqlBody).raw().all(params);
res.send(rows);
res.send(rows);
} catch (e: any) {
res.status(500).json({ error: e.message });
}
} else if (method === 'get') {
try {
const row = this.db.prepare(sql).raw().get(params);
return { data: row };
} catch (e: any) {
res.status(500).json({ error: e.message });
return { error: e.message };
}
} else {
res.status(500).json({ error: 'Unkown method value' });
Expand Down
5 changes: 0 additions & 5 deletions examples/sqlite-proxy/drizzle/20230202162455/migration.sql

This file was deleted.

36 changes: 0 additions & 36 deletions examples/sqlite-proxy/drizzle/20230202162455/snapshot.json

This file was deleted.

11 changes: 11 additions & 0 deletions examples/sqlite-proxy/drizzle/20230205140048/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE cities (
`id` integer PRIMARY KEY NOT NULL,
`name` text NOT NULL
);

CREATE TABLE users (
`id` integer PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`email` text NOT NULL,
`city_id` integer
);
64 changes: 64 additions & 0 deletions examples/sqlite-proxy/drizzle/20230205140048/snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"version": "4",
"dialect": "sqlite",
"id": "71c97670-a6f8-42e0-9a15-4991f89e2134",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"cities": {
"name": "cities",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"city_id": {
"name": "city_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {}
}
},
"enums": {}
}
22 changes: 13 additions & 9 deletions examples/sqlite-proxy/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import axios from 'axios';
import { eq } from 'drizzle-orm/expressions';
import { drizzle } from 'drizzle-orm/sqlite-proxy';
import { migrate } from 'drizzle-orm/sqlite-proxy/migrator';
import { users } from './schema';
import { cities, users } from './schema';

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

return { rows: rows.data };
} catch (e: any) {
console.error('Error from sqlite proxy server: ', e.response.data)
console.error('Error from sqlite proxy server: ', e.response.data);
return { rows: [] };
}
});
Expand All @@ -19,16 +20,19 @@ async function main() {
try {
await axios.post('http://localhost:3000/migrate', { queries });
} catch (e) {
console.log(e)
throw Error('Proxy server cannot run migrations')
console.log(e);
throw Error('Proxy server cannot run migrations');
}
}, { migrationsFolder: 'drizzle' });

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

const usersResponse = await db.select(users).all();
console.log(usersResponse);
const insertedUser = await db.insert(users).values({ id: 1, name: 'name', email: 'email', cityId: 1 }).run();
console.log('insertedUser: ', insertedUser);

const usersToCityResponse = await db.select(users).leftJoin(cities, eq(users.cityId, cities.id)).get();
console.log('usersToCityResponse: ', usersToCityResponse);
}

main();
main();
8 changes: 7 additions & 1 deletion examples/sqlite-proxy/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const users = sqliteTable('users12', {
export const users = sqliteTable('users', {
id: int('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
cityId: int('city_id'),
});

export const cities = sqliteTable('cities', {
id: int('id').primaryKey(),
name: text('name').notNull(),
});
7 changes: 7 additions & 0 deletions examples/sqlite-proxy/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ app.post('/query', (req, res) => {
} catch (e: any) {
res.status(500).json({ error: e.message });
}
}else if (method === 'get') {
try {
const row = db.prepare(sqlBody).raw().get(params);
return { data: row };
} catch (e: any) {
return { error: e.message };
}
} else {
res.status(500).json({ error: 'Unkown method value' });
}
Expand Down
Loading