Socket
Socket
Sign inDemoInstall

slonik

Package Overview
Dependencies
Maintainers
1
Versions
395
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

slonik - npm Package Compare versions

Comparing version 13.0.0 to 13.1.0

30

dist/templateTags/sql.js

@@ -20,4 +20,5 @@ "use strict";

namespace: 'sql'
});
}); // eslint-disable-next-line complexity
const sql = (parts, ...values) => {

@@ -37,3 +38,15 @@ let raw = '';

if (value && value.type === 'RAW_SQL' && typeof value.sql === 'string') {
raw += value.sql;
if (Array.isArray(value.values) && value.values.length) {
const fragmentValues = value.values;
raw += value.sql.replace(/\$(\d+)/, (match, g1) => {
return '$' + (parseInt(g1, 10) + bindings.length);
});
for (const fragmentValue of fragmentValues) {
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(fragmentValue), 'Unexpected set member type.');
bindings.push(fragmentValue);
}
} else {
raw += value.sql;
}
} else if (value && value.type === 'IDENTIFIER' && Array.isArray(value.names)) {

@@ -62,10 +75,14 @@ raw += value.names.map(identifierName => {

const multisetMemberSql = [];
let lastSetSize;
for (const set of value.sets) {
const placeholders = [];
(0, _invariant.default)(Array.isArray(set), 'Unexpected set shape.');
if (!Array.isArray(set)) {
throw new TypeError('Unexpected state.');
if (typeof lastSetSize === 'number' && lastSetSize !== set.length) {
throw new Error('Each set in a collection of sets must have an equal number of members.');
}
lastSetSize = set.length;
for (const member of set) {

@@ -108,6 +125,7 @@ placeholders.push('$' + ++placeholderIndex);

sql.raw = rawSql => {
sql.raw = (rawSql, values) => {
return {
sql: rawSql,
type: 'RAW_SQL'
type: 'RAW_SQL',
values: values || []
};

@@ -114,0 +132,0 @@ };

2

package.json

@@ -96,3 +96,3 @@ {

},
"version": "13.0.0"
"version": "13.1.0"
}

@@ -15,10 +15,8 @@ <a name="slonik"></a>

* Predominantly compatible with [node-postgres](https://github.com/brianc/node-postgres) (see [Incompatibilities with `node-postgres`](#incompatibilities-with-node-postgres)).
* [Convenience methods](#slonik-query-methods) with built-in assertions.
* [Middleware](#slonik-interceptors) support.
* [Syntax highlighting](#slonik-syntax-highlighting) (Atom plugin compatible with Slonik).
* [SQL injection guarding](#slonik-value-placeholders-tagged-template-literals).
* [Set interpolation](#set-interpolation).
* [Set interpolation](#slonik-value-placeholders-sql-set).
* Detail [logging](#slonik-debugging).
* [Parsing and logging of the auto_explain logs.](#logging-auto_explain).
* [Parsing and logging of the auto_explain logs](#logging-auto_explain).
* Built-in [asynchronous stack trace resolution](#log-stack-trace).

@@ -28,3 +26,4 @@ * [Safe connection pooling](#checking-out-a-client-from-the-connection-pool).

* [Mapped errors](#error-handling).
* [Transactions](#transactions).
* [Transactions](#slonik-query-methods-transaction).
* [Atom plugin](#slonik-syntax-highlighting).
* [ESLint plugin](https://github.com/gajus/eslint-plugin-sql).

@@ -50,36 +49,8 @@

* [Logging `auto_explain`](#slonik-recipes-logging-auto_explain)
* [Incompatibilities with `node-postgres`](#slonik-incompatibilities-with-node-postgres)
* [Conventions](#slonik-conventions)
* [No multiline values](#slonik-conventions-no-multiline-values)
* [Value placeholders](#slonik-value-placeholders)
* [Tagged template literals](#slonik-value-placeholders-tagged-template-literals)
* [`sql.set`](#slonik-value-placeholders-sql-set)
* [`sql.multiset`](#slonik-value-placeholders-sql-multiset)
* [`sql.identifier`](#slonik-value-placeholders-sql-identifier)
* [`sql.raw`](#slonik-value-placeholders-sql-raw)
* [Query methods](#slonik-query-methods)
* [`any`](#slonik-query-methods-any)
* [`anyFirst`](#slonik-query-methods-anyfirst)
* [`insert`](#slonik-query-methods-insert)
* [`many`](#slonik-query-methods-many)
* [`manyFirst`](#slonik-query-methods-manyfirst)
* [`maybeOne`](#slonik-query-methods-maybeone)
* [`maybeOneFirst`](#slonik-query-methods-maybeonefirst)
* [`one`](#slonik-query-methods-one)
* [`oneFirst`](#slonik-query-methods-onefirst)
* [`query`](#slonik-query-methods-query)
* [`transaction`](#slonik-query-methods-transaction)
* [Error handling](#slonik-error-handling)
* [Handling `NotFoundError`](#slonik-error-handling-handling-notfounderror)
* [Handling `DataIntegrityError`](#slonik-error-handling-handling-dataintegrityerror)
* [Handling `NotNullIntegrityConstraintViolationError`](#slonik-error-handling-handling-notnullintegrityconstraintviolationerror)
* [Handling `ForeignKeyIntegrityConstraintViolationError`](#slonik-error-handling-handling-foreignkeyintegrityconstraintviolationerror)
* [Handling `UniqueIntegrityConstraintViolationError`](#slonik-error-handling-handling-uniqueintegrityconstraintviolationerror)
* [Handling `CheckIntegrityConstraintViolationError`](#slonik-error-handling-handling-checkintegrityconstraintviolationerror)
* [Types](#slonik-types)
* [Debugging](#slonik-debugging)
* [Logging](#slonik-debugging-logging)
* [Log stack trace](#slonik-debugging-log-stack-trace)
* [Syntax highlighting](#slonik-syntax-highlighting)
* [Atom](#slonik-syntax-highlighting-atom)
* [Using `sql.raw` to generate dynamic queries](#slonik-recipes-using-sql-raw-to-generate-dynamic-queries)
* [Logs query parameter values](#logs-query-parameter-values)
* [Conventions](#logs-query-parameter-values-conventions)
* [No multiline values](#logs-query-parameter-values-conventions-no-multiline-values)
* [Syntax highlighting](#logs-query-parameter-values-syntax-highlighting)
* [Atom](#logs-query-parameter-values-syntax-highlighting-atom)

@@ -509,13 +480,23 @@

<a name="slonik-recipes-using-sql-raw-to-generate-dynamic-queries"></a>
### Using <code>sql.raw</code> to generate dynamic queries
<a name="slonik-incompatibilities-with-node-postgres"></a>
## Incompatibilities with <code>node-postgres</code>
[`sql.raw`](#sqlraw) can be used to generate fragments of an arbitrary SQL that are interpolated into the main query, e.g.
* `timestamp` and `timestamp with time zone` returns UNIX timestamp in milliseconds.
* Connection pool `connect()` method requires that connection is restricted to a single promise routine (see [Checking out a client from the connection pool](#checking-out-a-client-from-the-connection-pool)).
```js
const uniquePairs = [
['a', 1],
['b', 2]
];
<a name="slonik-conventions"></a>
let placeholderIndex = 1;
const whereConditionSql = uniquePairs
.map(() => {
return needleColumns
.map((column) => {
return column + ' =
## Conventions
<a name="slonik-conventions-no-multiline-values"></a>
### No multiline values

@@ -529,3 +510,3 @@

```sql
```js
// Do not do this

@@ -538,3 +519,3 @@ connection.query(sql`INSERT INTO foo (bar) VALUES ('\n')`);

```sql
```js
connection.query(sql`INSERT INTO foo (bar) VALUES (${'\n'})`);

@@ -544,6 +525,4 @@

<a name="slonik-value-placeholders"></a>
## Value placeholders
<a name="slonik-value-placeholders-tagged-template-literals"></a>
### Tagged template literals

@@ -566,3 +545,3 @@

Produces:
The above is equivalent to evaluating:

@@ -576,7 +555,11 @@ ```sql

and is evaluated with 'baz' value binding.
query with 'baz' value binding.
<a name="slonik-value-placeholders-sql-set"></a>
### <code>sql.set</code>
### `sql.set`
```js
(members: $ReadOnlyArray<PrimitiveValueExpressionType>) => SetSqlTokenType;
```
`sql.set` is used to create a typed row construct (or a set, depending on the context), e.g.

@@ -593,10 +576,21 @@

```sql
SELECT ($1, $2, $3)
```js
{
sql: 'SELECT ($1, $2, $3)',
values: [
1,
2,
3
]
}
```
<a name="slonik-value-placeholders-sql-multiset"></a>
### <code>sql.multiset</code>
### `sql.multiset`
```js
(sets: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>) => MultisetSqlTokenType;
```
`sql.multiset` is used to create a comma-separated list of typed row constructs, e.g.

@@ -608,3 +602,3 @@

[1, 2, 3],
[1, 2, 3]
[4, 5, 6]
])}

@@ -617,10 +611,24 @@ `);

```sql
SELECT ($1, $2, $3), ($4, $5, $6)
```js
{
sql: 'SELECT ($1, $2, $3), ($4, $5, $6)',
values: [
1,
2,
3,
4,
5,
6
]
}
```
<a name="slonik-value-placeholders-sql-identifier"></a>
### <code>sql.identifier</code>
### `sql.identifier`
```js
(names: $ReadOnlyArray<string>) => IdentifierTokenType;
```
[Delimited identifiers](https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS) are created by enclosing an arbitrary sequence of characters in double-quotes ("). To create create a delimited identifier, create an `sql` tag function placeholder value using `sql.identifier`, e.g.

@@ -638,11 +646,17 @@

```sql
SELECT 1
FROM "bar"."bar"
```js
{
sql: 'SELECT 1 FROM "bar"."bar"',
values: []
}
```
<a name="slonik-value-placeholders-sql-raw"></a>
### <code>sql.raw</code>
### `sql.raw`
```js
(rawSql: string, values?: $ReadOnlyArray<PrimitiveValueExpressionType>) => RawSqlTokenType;
```
Raw/ dynamic SQL can be inlined using `sql.raw`, e.g.

@@ -660,14 +674,35 @@

```sql
SELECT 1
FROM "bar"
```js
{
sql: 'SELECT 1 FROM "bar"',
values: []
}
```
The second parameter of the `sql.raw` can be used to bind values, e.g.
<a name="slonik-query-methods"></a>
```js
sql`
SELECT ${sql.raw('$1', [1])}
`;
```
Produces:
```js
{
sql: 'SELECT $1',
values: [
1
]
}
```
## Query methods
<a name="slonik-query-methods-any"></a>
### <code>any</code>
### `any`

@@ -685,4 +720,3 @@ Returns result rows.

<a name="slonik-query-methods-anyfirst"></a>
### <code>anyFirst</code>
### `anyFirst`

@@ -700,4 +734,3 @@ Returns value of the first column of every row in the result set.

<a name="slonik-query-methods-insert"></a>
### <code>insert</code>
### `insert`

@@ -717,4 +750,3 @@ Used when inserting 1 row.

<a name="slonik-query-methods-many"></a>
### <code>many</code>
### `many`

@@ -732,4 +764,3 @@ Returns result rows.

<a name="slonik-query-methods-manyfirst"></a>
### <code>manyFirst</code>
### `manyFirst`

@@ -748,4 +779,3 @@ Returns value of the first column of every row in the result set.

<a name="slonik-query-methods-maybeone"></a>
### <code>maybeOne</code>
### `maybeOne`

@@ -766,4 +796,3 @@ Selects the first row from the result.

<a name="slonik-query-methods-maybeonefirst"></a>
### <code>maybeOneFirst</code>
### `maybeOneFirst`

@@ -785,4 +814,3 @@ Returns value of the first column from the first row.

<a name="slonik-query-methods-one"></a>
### <code>one</code>
### `one`

@@ -810,4 +838,3 @@ Selects the first row from the result.

<a name="slonik-query-methods-onefirst"></a>
### <code>oneFirst</code>
### `oneFirst`

@@ -829,9 +856,7 @@ Returns value of the first column from the first row.

<a name="slonik-query-methods-query"></a>
### <code>query</code>
### `query`
API and the result shape are equivalent to [`pg#query`](https://github.com/brianc/node-postgres).
<a name="slonik-query-methods-transaction"></a>
### <code>transaction</code>
### `transaction`

@@ -855,3 +880,2 @@ `transaction` method is used wrap execution of queries in `START TRANSACTION` and `COMMIT` or `ROLLBACK`. `COMMIT` is called if the transaction handler returns a promise that resolves; `ROLLBACK` is called otherwise.

<a name="slonik-error-handling"></a>
## Error handling

@@ -876,4 +900,3 @@

<a name="slonik-error-handling-handling-notfounderror"></a>
### Handling <code>NotFoundError</code>
### Handling `NotFoundError`

@@ -903,4 +926,3 @@ To handle the case where query returns less than one row, catch `NotFoundError` error.

<a name="slonik-error-handling-handling-dataintegrityerror"></a>
### Handling <code>DataIntegrityError</code>
### Handling `DataIntegrityError`

@@ -928,23 +950,18 @@ To handle the case where the data result does not match the expectations, catch `DataIntegrityError` error.

<a name="slonik-error-handling-handling-notnullintegrityconstraintviolationerror"></a>
### Handling <code>NotNullIntegrityConstraintViolationError</code>
### Handling `NotNullIntegrityConstraintViolationError`
`NotNullIntegrityConstraintViolationError` is thrown when Postgres responds with [`unique_violation`](https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html) (`23502`) error.
<a name="slonik-error-handling-handling-foreignkeyintegrityconstraintviolationerror"></a>
### Handling <code>ForeignKeyIntegrityConstraintViolationError</code>
### Handling `ForeignKeyIntegrityConstraintViolationError`
`ForeignKeyIntegrityConstraintViolationError` is thrown when Postgres responds with [`unique_violation`](https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html) (`23503`) error.
<a name="slonik-error-handling-handling-uniqueintegrityconstraintviolationerror"></a>
### Handling <code>UniqueIntegrityConstraintViolationError</code>
### Handling `UniqueIntegrityConstraintViolationError`
`UniqueIntegrityConstraintViolationError` is thrown when Postgres responds with [`unique_violation`](https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html) (`23505`) error.
<a name="slonik-error-handling-handling-checkintegrityconstraintviolationerror"></a>
### Handling <code>CheckIntegrityConstraintViolationError</code>
### Handling `CheckIntegrityConstraintViolationError`
`CheckIntegrityConstraintViolationError` is thrown when Postgres responds with [`unique_violation`](https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html) (`23514`) error.
<a name="slonik-types"></a>
## Types

@@ -987,6 +1004,4 @@

<a name="slonik-debugging"></a>
## Debugging
<a name="slonik-debugging-logging"></a>
### Logging

@@ -1003,2 +1018,3 @@

```bash
<a name="logs-query-parameter-values"></a>
# Logs query parameter values

@@ -1009,3 +1025,2 @@ export SLONIK_LOG_VALUES=true

<a name="slonik-debugging-log-stack-trace"></a>
### Log stack trace

@@ -1026,6 +1041,4 @@

<a name="slonik-syntax-highlighting"></a>
## Syntax highlighting
<a name="slonik-syntax-highlighting-atom"></a>
### Atom

@@ -1044,1 +1057,93 @@

For more information, refer to the [JavaScript Tagged Template Literal Grammar Extensions](https://github.com/gandm/language-babel#javascript-tagged-template-literal-grammar-extensions) documentation of `language-babel` package.
+ placeholderIndex++;
})
.join(' AND ');
})
.join(' OR ');
const values = [];
for (const pairValues of uniquePairs) {
values.push(...pairValues);
}
const query = sql`
SELECT
id
FROM foo
WHERE
${sql.raw(whereConditionSql, values)}
`;
await connection.any(query);
```
In the above example, `query` is:
```js
{
sql: 'SELECT id FROM foo WHERE (a = $1 AND b = $2) OR (a = $3 AND b = $4)',
values: [
'a',
1,
'b',
2
]
}
```
Multiple `sql.raw` fragments can be used to create a query.
<a name="logs-query-parameter-values-conventions"></a>
## Conventions
<a name="logs-query-parameter-values-conventions-no-multiline-values"></a>
### No multiline values
Slonik will strip all comments and line-breaks from a query before processing it.
This makes logging of the queries easier.
The implication is that your query cannot contain values that include a newline character, e.g.
```js
// Do not do this
connection.query(sql`INSERT INTO foo (bar) VALUES ('\n')`);
```
If you want to communicate a value that includes a multiline character, use value placeholder interpolation, e.g.
```js
connection.query(sql`INSERT INTO foo (bar) VALUES (${'\n'})`);
```
⊂⊂C:5⊃⊃
⊂⊂C:6⊃⊃
⊂⊂C:7⊃⊃
⊂⊂C:8⊃⊃
<a name="logs-query-parameter-values-syntax-highlighting"></a>
## Syntax highlighting
<a name="logs-query-parameter-values-syntax-highlighting-atom"></a>
### Atom
Using [Atom](https://atom.io/) IDE you can leverage the [`language-babel`](https://github.com/gandm/language-babel) package in combination with the [`language-sql`](https://github.com/atom/language-sql) to enable highlighting of the SQL strings in the codebase.
![Syntax highlighting in Atom](./.README/atom-syntax-highlighting.png)
To enable highlighting, you need to:
1. Install `language-babel` and `language-sql` packages.
1. Configure `language-babel` "JavaScript Tagged Template Literal Grammar Extensions" setting to use `language-sql` to highlight template literals with `sql` tag (configuration value: `sql:source.sql`).
1. Use [`sql` helper to construct the queries](https://github.com/gajus/slonik#tagged-template-literals).
For more information, refer to the [JavaScript Tagged Template Literal Grammar Extensions](https://github.com/gandm/language-babel#javascript-tagged-template-literal-grammar-extensions) documentation of `language-babel` package.

@@ -5,6 +5,9 @@ // @flow

import type {
ValueExpressionType,
IdentifierTokenType,
MultisetSqlTokenType,
PrimitiveValueExpressionType,
RawSqlTokenType,
TaggledTemplateLiteralInvocationType
SetSqlTokenType,
TaggledTemplateLiteralInvocationType,
ValueExpressionType
} from '../types';

@@ -21,2 +24,3 @@ import {

// eslint-disable-next-line complexity
const sql = (parts: $ReadOnlyArray<string>, ...values: $ReadOnlyArray<ValueExpressionType>): TaggledTemplateLiteralInvocationType => {

@@ -39,3 +43,17 @@ let raw = '';

if (value && value.type === 'RAW_SQL' && typeof value.sql === 'string') {
raw += value.sql;
if (Array.isArray(value.values) && value.values.length) {
const fragmentValues = value.values;
raw += value.sql.replace(/\$(\d+)/, (match, g1) => {
return '$' + (parseInt(g1, 10) + bindings.length);
});
for (const fragmentValue of fragmentValues) {
invariant(isPrimitiveValueExpression(fragmentValue), 'Unexpected set member type.');
bindings.push(fragmentValue);
}
} else {
raw += value.sql;
}
} else if (value && value.type === 'IDENTIFIER' && Array.isArray(value.names)) {

@@ -72,9 +90,15 @@ raw += value.names

let lastSetSize;
for (const set of value.sets) {
const placeholders = [];
if (!Array.isArray(set)) {
throw new TypeError('Unexpected state.');
invariant(Array.isArray(set), 'Unexpected set shape.');
if (typeof lastSetSize === 'number' && lastSetSize !== set.length) {
throw new Error('Each set in a collection of sets must have an equal number of members.');
}
lastSetSize = set.length;
for (const member of set) {

@@ -121,10 +145,11 @@ placeholders.push('$' + ++placeholderIndex);

sql.raw = (rawSql: string): RawSqlTokenType => {
sql.raw = (rawSql: string, values?: $ReadOnlyArray<PrimitiveValueExpressionType>): RawSqlTokenType => {
return {
sql: rawSql,
type: 'RAW_SQL'
type: 'RAW_SQL',
values: values || []
};
};
sql.set = (members) => {
sql.set = (members: $ReadOnlyArray<PrimitiveValueExpressionType>): SetSqlTokenType => {
return {

@@ -136,3 +161,3 @@ members,

sql.multiset = (sets) => {
sql.multiset = (sets: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>): MultisetSqlTokenType => {
return {

@@ -139,0 +164,0 @@ sets,

@@ -140,3 +140,4 @@ // @flow

sql: string,
type: 'RAW_SQL'
type: 'RAW_SQL',
values: $ReadOnlyArray<PrimitiveValueExpressionType>
|};

@@ -154,3 +155,3 @@

type PrimitiveValueExpressionType = string | number | boolean | null;
export type PrimitiveValueExpressionType = string | number | boolean | null;

@@ -157,0 +158,0 @@ export type ValueExpressionType =

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc