From dd0afc62cf5f646431311030ab5c740dcb20f7c3 Mon Sep 17 00:00:00 2001 From: yannvgn Date: Mon, 1 Oct 2018 15:09:18 +0200 Subject: [PATCH 1/2] add sql.raw --- lib/pg.ts | 13 +++++-------- lib/sql.ts | 8 +++++--- lib/utils.ts | 4 ++++ readme.md | 33 ++++++++++++++++++++++----------- test/pg.test.ts | 10 ++++++++++ test/sql.test.ts | 8 ++++++++ 6 files changed, 54 insertions(+), 22 deletions(-) diff --git a/lib/pg.ts b/lib/pg.ts index 4d1f541..37f55da 100644 --- a/lib/pg.ts +++ b/lib/pg.ts @@ -1,12 +1,7 @@ import _sql = require('./sql') -import { - IPGQueryable, - IPGQueryConfig, - IPGQueryResult, - TemplateLiteralFunc -} from './utils' - -type PGSql = TemplateLiteralFunc & { +import { IPGQueryable, IPGQueryResult, Sql, TemplateLiteralFunc } from './utils' + +type PGSql = Sql & { query: ( db: IPGQueryable ) => TemplateLiteralFunc> @@ -17,6 +12,8 @@ type PGSql = TemplateLiteralFunc & { const sql = ((chains, ...expressions) => _sql(chains, ...expressions)) as PGSql +sql.raw = rawData => _sql.raw(rawData) + sql.query = db => (chains, ...expressions) => db.query(_sql(chains, ...expressions)) diff --git a/lib/sql.ts b/lib/sql.ts index 5054c82..272f40b 100644 --- a/lib/sql.ts +++ b/lib/sql.ts @@ -1,4 +1,4 @@ -import { IPGQueryConfig, SqlContainer, TemplateLiteralFunc } from './utils' +import { IPGQueryConfig, Sql, SqlContainer } from './utils' function sqlText( count: number, @@ -39,7 +39,9 @@ function sqlText( } } -const sql: TemplateLiteralFunc = (chains, ...expressions) => - sqlText(1, chains, expressions) +const sql: Sql = ((chains, ...expressions) => + sqlText(1, chains, expressions)) as Sql + +sql.raw = rawData => sqlText(1, [rawData], []) export = sql diff --git a/lib/utils.ts b/lib/utils.ts index d9dc659..fa37c56 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -24,6 +24,10 @@ export interface IPGQueryConfig { values: any[] } +export type Sql = TemplateLiteralFunc & { + raw: (rawData: string) => IPGQueryConfig +} + export interface IPGQueryResult { rowCount: number rows: any[] diff --git a/readme.md b/readme.md index 3fd43fa..5b4e64c 100644 --- a/readme.md +++ b/readme.md @@ -40,14 +40,14 @@ const query = sql` and year <= ${yearRange[1]} ` -// query looks like this : +// query looks like this: // { // text: 'select * from books where author = $1 and year = $2', // values: [1983, 1992] // } ``` -You can also use conditions : +You can also use conditions: ```js const sql = require('@sequencework/sql') @@ -60,13 +60,13 @@ const findBookByAuthor = author => sql` } ` -// findBookByAuthor() looks like this : +// findBookByAuthor() looks like this: // { // text: 'select * from books', // values: [] // } -// findBookByAuthor('steinbeck') looks like this : +// findBookByAuthor('steinbeck') looks like this: // { // text: 'select * from books where author = $1', // values: ['steinbeck'] @@ -91,9 +91,18 @@ sql` ` ``` +It's also possible to pass raw, unescaped data to your queries. For that, use `sql.raw`: + +```js +const tableName = 'books' +const query = sql`select * from ${sql.raw(tableName)}` +``` + +💥 Please, be careful! Remember that the raw values won't be replaced by a placeholder and thus won't be escaped! + ### Example with [node-postgres](https://github.com/brianc/node-postgres) -We start by creating a function : +We start by creating a function: ```js // movies.js @@ -124,7 +133,7 @@ const db = new Pool() module.exports = db ``` -Finally, we connect everything : +Finally, we connect everything: ```js // main.js @@ -140,7 +149,7 @@ const main = async () => { main() ``` -We can even create a **transaction** (useless in this example, but it's just to show that our previous function is reusable) : +We can even create a **transaction** (useless in this example, but it's just to show that our previous function is reusable): ```js const main = async () => { @@ -165,7 +174,7 @@ const main = async () => { #### Shorthand for postgres -Since we ❤️ [node-postgres](https://github.com/brianc/node-postgres) so much, we created shorthands and helpers for it : +Since we ❤️ [node-postgres](https://github.com/brianc/node-postgres) so much, we created shorthands and helpers for it: ```js const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sql/pg @@ -173,13 +182,15 @@ const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sq // main export stays the same const query = sql`select * from movies where id = ${id}` -// default pg result object : https://node-postgres.com/api/result +// default pg result object: https://node-postgres.com/api/result const { rows, rowCount } = await sql.query(db)`select * from movies` // helpers const movies = await sql.many(db)`select * from movies` const movie = await sql.one(db)`select * from movies where id = ${id}` -const nbMovie = await sql.count(db)`update from movies set name = ${name} where id = ${id}` +const nbMovie = await sql.count( + db +)`update from movies set name = ${name} where id = ${id}` ``` You can then rewrite the previous `listMoviesByYear` function in a much more concise way 😎 @@ -214,7 +225,7 @@ const query = sql` ### More -This package is inspired by the great [sql-template-strings](https://github.com/felixfbecker/node-sql-template-strings). Some interesting features that we were missing : +This package is inspired by the great [sql-template-strings](https://github.com/felixfbecker/node-sql-template-strings). Some interesting features that we were missing: - nested `sql` tags - ignore `undefined` expressions in `sql` diff --git a/test/pg.test.ts b/test/pg.test.ts index 77fbff5..0784c85 100644 --- a/test/pg.test.ts +++ b/test/pg.test.ts @@ -46,3 +46,13 @@ test('sql.count should return rowCount', async () => { const nbBooks = await sql.count(db)`select * from books where read = ${false}` expect(nbBooks).toBe(sampleBooks.length) }) + +test('sql.raw should work with pg shorthand', async () => { + const tableName = 'books' + const { rows, rowCount, oid } = await sql.query(db)`select * from ${sql.raw( + tableName + )} where read = ${false}` + expect(rows).toBe(sampleBooks) + expect(rowCount).toBe(sampleBooks.length) + expect(oid).toBe(1) +}) diff --git a/test/sql.test.ts b/test/sql.test.ts index 3fe82a9..775030f 100644 --- a/test/sql.test.ts +++ b/test/sql.test.ts @@ -102,3 +102,11 @@ test('json as query parameter', () => { expect(query.values).toHaveLength(1) expect(query.values[0]).toBe(jsonValue) }) + +test('query with raw data', () => { + const tableName = 'books' + const query = sql`select * from ${sql.raw(tableName)} where read = ${false}` + expect(trimSpaces(query.text)).toBe('select * from books where read = $1') + expect(query.values).toHaveLength(1) + expect(query.values[0]).toBe(false) +}) From 5efe81fdbd4f169842feda640f937922fc2b804b Mon Sep 17 00:00:00 2001 From: yannvgn Date: Mon, 1 Oct 2018 15:12:29 +0200 Subject: [PATCH 2/2] update readme --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 5b4e64c..d458c0d 100644 --- a/readme.md +++ b/readme.md @@ -182,6 +182,10 @@ const sql = require('@sequencework/sql/pg') // ⚠️ we import @sequencework/sq // main export stays the same const query = sql`select * from movies where id = ${id}` +// sql.raw is also there +const booksTable = 'books' +const booksQuery = sql`select * from ${sql.raw(booksTable)}` + // default pg result object: https://node-postgres.com/api/result const { rows, rowCount } = await sql.query(db)`select * from movies`