Skip to content

Commit

Permalink
Refactor query return type
Browse files Browse the repository at this point in the history
  • Loading branch information
brombal committed Feb 5, 2024
1 parent 8497c98 commit 7849fe6
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 78 deletions.
39 changes: 21 additions & 18 deletions README.md
Expand Up @@ -151,12 +151,13 @@ For documentation on the driver-specific setup, check out their readme pages:
To execute a query, simply use the tag with a SQL query, and `await` the result:

```ts
const [rows, info] = await sql<User>`SELECT * FROM users`;
const users = await sql<User>`SELECT * FROM users`;
```

The result is a 2-tuple of `[rows, info]`, where `rows` is an array of the query results, and `info`
is any additional information about the query that the driver provides (e.g. the column information,
or number of affected rows, etc; this value is defined by the underlying database driver).
The result is an array containing the query results. The array also has an additional property
`info`, which has additional information about the query that the driver provides (e.g. the column
information, or number of affected rows, etc; this value is defined by the underlying database
driver).

## Cursors

Expand Down Expand Up @@ -188,10 +189,11 @@ interface User {
// etc.
}

const [users, info] = await sql<User>`SELECT * FROM users`;
const users = await sql<User>`SELECT * FROM users`;
```

`users` will be of type User[]. The type of `info` is defined by the driver.
`users` will be of type `User[]`, with an additional property `users.info` which contains additional
query execution information (the type is defined by the driver).

## Building SQL queries

Expand All @@ -201,7 +203,7 @@ Values will automatically be parameterized when they are interpolated into the q
string.

```js
const [rows] = await sql`SELECT * FROM users WHERE id = ${userId}`;
const users = await sql`SELECT * FROM users WHERE id = ${userId}`;
```

"Parameterized" means that they are replaced by a placeholder (e.g. `?` or `$1`) and passed to the
Expand All @@ -213,7 +215,7 @@ prevents SQL injection attacks.
>
> ```ts
> const userId = undefined;
> const [rows] = await sql`SELECT * FROM users WHERE id = ${userId}`;
> const users = await sql`SELECT * FROM users WHERE id = ${userId}`;
> // Executes an invalid statement:
> // `SELECT * FROM users WHERE id = `
> // with no query parameters!
Expand All @@ -231,7 +233,7 @@ tag's `.id()` method:

```js
const table = 'users';
const [tableValues] = await sql`SELECT * FROM ${sql.id(table)}`;
const users = await sql`SELECT * FROM ${sql.id(table)}`;
```

Identifiers are escaped appropriately by the driver, but are not parameterized.
Expand All @@ -241,7 +243,7 @@ Identifiers are escaped appropriately by the driver, but are not parameterized.
To nest SQL expressions, just embed a `sql` tag expression:

```js
const [rows] = await sql`
const users = await sql`
SELECT *
FROM users
${userId ? sql`WHERE id = ${userId}` : undefined}
Expand All @@ -256,7 +258,7 @@ To embed variables directly without parameterizing them, use the tag's `.raw()`

```js
const whereClause = "status = 'active'";
const [rows] = await sql`
const users = await sql`
SELECT *
FROM users
where ${sql.raw(whereClause)}
Expand Down Expand Up @@ -300,7 +302,7 @@ if (userId) {
// Join the query parts, separating each with a newline:
const query = sql.join(queryParts, '\n');

const [rows] = await query;
const users = await query;
```

## SQL expression helpers
Expand All @@ -320,7 +322,7 @@ and will be grouped in parentheses appropriately. `undefined` values are omitted
with other falsy values (e.g. `null`, `false`, `0`).

```js
const [rows] = await sql`
const users = await sql`
SELECT *
FROM users
WHERE ${sql.and(
Expand Down Expand Up @@ -355,7 +357,7 @@ const user = {
};

// Updates only the `name` and `status` columns:
const [rows] = await sql`
await sql`
UPDATE users
SET ${sql.setValues(user, 'name', 'status')}
WHERE id = ${user.id}
Expand Down Expand Up @@ -397,7 +399,7 @@ To generate an expression like `` `column` IN (value1, value2, etc) ``, use the
method:

```js
const [rows] = await sql`SELECT * FROM users WHERE ${sql.in('id', [1, 2, 3])}`;
const users = await sql`SELECT * FROM users WHERE ${sql.in('id', [1, 2, 3])}`;

// SELECT * FROM users WHERE `id` IN (?, ?, ?)
// with parameters: [1, 2, 3]
Expand All @@ -411,7 +413,7 @@ To join (concatenate) an array of values or expressions, use the tag's `.join()`

```js
const ids = [1, 2, 3];
const [rows] = await sql`SELECT * FROM users WHERE id IN (${sql.join(ids)})`;
const users = await sql`SELECT * FROM users WHERE id IN (${sql.join(ids)})`;

// SELECT * FROM users WHERE id IN (?, ?, ?)
// with parameters: [1, 2, 3]
Expand All @@ -421,7 +423,7 @@ Values will be joined with a comma by default, but you can pass a specific separ
argument:

```js
const [rows] = await sql`
const users = await sql`
SELECT * FROM users
WHERE ${sql.join(
[
Expand Down Expand Up @@ -472,6 +474,7 @@ The event object has the following properties:
- `params` - The query parameters.
- `result` - The query result.
- `info` - Additional information about the query (e.g. column definitions, rows affected, etc).
This is provided by the database driver.
- `ms` - The query execution time in milliseconds.

> Note that this event is not emitted for cursors.
Expand Down Expand Up @@ -590,7 +593,7 @@ const db = new CoolDbConnection({
const sql = createCoolDbTag(db);

// Query!
const [rows, info] = await sql`SELECT * FROM users WHERE id = ${userId}`;
const users = await sql`SELECT * FROM users WHERE id = ${userId}`;
```

## Contributing
Expand Down
8 changes: 6 additions & 2 deletions core/SqlQuery.ts
Expand Up @@ -2,7 +2,7 @@ import { _driver, SqlTag } from './SqlTag';

export type SqlExpression = SqlQuery<never, never, never>;

export type SqlQueryResult<TResult, TQueryInfo> = [TResult[], TQueryInfo, string, any[]];
export type SqlQueryResult<TResult, TQueryInfo> = TResult[] & { query: string; params: any[]; info: TQueryInfo };

export class SqlQuery<TResult, TQueryInfo, TCursorOptions> extends Promise<
SqlQueryResult<TResult, TQueryInfo>
Expand All @@ -27,7 +27,11 @@ export class SqlQuery<TResult, TQueryInfo, TCursorOptions> extends Promise<
this.sqlTag.emit('beforeQuery', { queryText, params });
return this.sqlTag[_driver].query(queryText, params).then(([rows, queryInfo]) => {
this.sqlTag.emit('afterQuery', { queryText, params, rows, queryInfo, ms: performance.now() - now });
return onfulfilled!([rows, queryInfo, queryText, params]);
const result = rows as SqlQueryResult<TResult, TQueryInfo>;
result.query = queryText;
result.params = params;
result.info = queryInfo;
return onfulfilled!(result);
}, onrejected);
}

Expand Down
2 changes: 1 addition & 1 deletion core/package.json
@@ -1,6 +1,6 @@
{
"name": "@sqltags/core",
"version": "0.0.25",
"version": "0.0.26",
"description": "Safely create & execute parameterized SQL queries using template strings 🔧✨ minimal API and works with any db driver (pg, mysql, sqlite, etc).",
"license": "MIT",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Expand Up @@ -20,6 +20,6 @@ services:
POSTGRES_PASSWORD: password
POSTGRES_DB: db
ports:
- 5432:5432
- 5431:5432
volumes:
- ./data/pg:/var/lib/postgresql/data
2 changes: 1 addition & 1 deletion drivers/mysql/package.json
@@ -1,6 +1,6 @@
{
"name": "@sqltags/mysql",
"version": "0.0.25",
"version": "0.0.26",
"description": "MySQL driver for sqltags (@sqltags/core) 🔧✨ Safely create & execute parameterized SQL queries using template strings",
"license": "MIT",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion drivers/postgres/package.json
@@ -1,6 +1,6 @@
{
"name": "@sqltags/pg",
"version": "0.0.25",
"version": "0.0.26",
"description": "PostgreSQL driver for SqlTags (@sqltags/core) 🔧✨ Safely create & execute parameterized SQL queries using template strings",
"license": "MIT",
"author": {
Expand Down
2 changes: 1 addition & 1 deletion drivers/sqlite/package.json
@@ -1,6 +1,6 @@
{
"name": "@sqltags/sqlite",
"version": "0.0.25",
"version": "0.0.26",
"description": "SQLite driver for sqltags (@sqltags/core) 🔧✨ Safely create & execute parameterized SQL queries using template strings",
"license": "MIT",
"author": {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions test/driver-tests.ts
Expand Up @@ -27,8 +27,8 @@ export function executeDriverTests(testPrefix: string, getSql: () => SqlTag<any,
INSERT INTO users ${sql.insertValues(testUsers)}
`;

const [users, info] = await sql<User>`SELECT * FROM users`;
expect(users).toEqual(testUsers);
const users = await sql<User>`SELECT * FROM users`;
expect([...users]).toEqual(testUsers);
});

test(`${testPrefix}: cursor`, async () => {
Expand Down
6 changes: 3 additions & 3 deletions test/mysql.test.ts
Expand Up @@ -47,8 +47,8 @@ describe('mysql', () => {
});

test('insert/select values', async () => {
const [, info] = await sql`SELECT 1`;
expect(info.length).toBe(1);
const rows = await sql`SELECT 1`;
expect(rows.info.length).toBe(1);
});

executeDriverTests('mysql', () => sql);
Expand Down Expand Up @@ -77,7 +77,7 @@ describe('mysql', () => {

// load data using regular query several times
for (let i = 0; i < 5; i++) {
const [userRows] = await sql<User>`SELECT * FROM users`;
const userRows = await sql<User>`SELECT * FROM users`;

const bytesUsed: number[] = [];

Expand Down
12 changes: 6 additions & 6 deletions test/postgres.test.ts
Expand Up @@ -8,7 +8,7 @@ describe('pg client', () => {
user: 'user',
password: 'password',
database: 'db',
port: 5432,
port: 5431,
});

let sql: ReturnType<typeof createPgTag>;
Expand Down Expand Up @@ -39,8 +39,8 @@ describe('pg client', () => {
});

test('insert/select values', async () => {
const [_rows, info] = await sql`SELECT 1`;
expect(info.fields.length).toEqual(1);
const rows = await sql`SELECT 1`;
expect(rows.info.fields.length).toEqual(1);
});

executeDriverTests('pg client', () => sql);
Expand All @@ -52,7 +52,7 @@ describe('pg pool', () => {
user: 'user',
password: 'password',
database: 'db',
port: 5432,
port: 5431,
});

let sql: ReturnType<typeof createPgTag>;
Expand Down Expand Up @@ -82,8 +82,8 @@ describe('pg pool', () => {
});

test('insert/select values', async () => {
const [_rows, info] = await sql`SELECT 1`;
expect(info.fields.length).toEqual(1);
const rows = await sql`SELECT 1`;
expect(rows.info.fields.length).toEqual(1);
});

executeDriverTests('pg pool', () => sql);
Expand Down

0 comments on commit 7849fe6

Please sign in to comment.