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 11.2.0 to 12.0.0

dist/factories/createClientConfiguration.js

10

dist/binders/bindPool.js

@@ -26,4 +26,6 @@ "use strict";

if (clientConfiguration.onConnect) {
await clientConfiguration.onConnect(boundConnection);
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.afterPoolConnection) {
await interceptor.afterPoolConnection(boundConnection);
}
}

@@ -36,5 +38,3 @@

} finally {
const interceptors = clientConfiguration && clientConfiguration.interceptors || [];
for (const interceptor of interceptors) {
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.beforePoolConnectionRelease) {

@@ -41,0 +41,0 @@ await interceptor.beforePoolConnectionRelease(boundConnection);

@@ -18,8 +18,2 @@ "use strict";

});
Object.defineProperty(exports, "bindSingleConnection", {
enumerable: true,
get: function () {
return _bindSingleConnection.default;
}
});
Object.defineProperty(exports, "bindTransactionConnection", {

@@ -36,4 +30,2 @@ enumerable: true,

var _bindSingleConnection = _interopRequireDefault(require("./bindSingleConnection"));
var _bindTransactionConnection = _interopRequireDefault(require("./bindTransactionConnection"));

@@ -40,0 +32,0 @@

@@ -6,3 +6,3 @@ "use strict";

});
exports.SLONIK_LOG_VALUES = exports.SLONIK_LOG_STACK_TRACE = exports.SLONIK_LOG_NORMALISED = void 0;
exports.SLONIK_LOG_VALUES = exports.SLONIK_LOG_STACK_TRACE = void 0;

@@ -14,4 +14,2 @@ var _boolean = _interopRequireDefault(require("boolean"));

/* eslint-disable no-process-env */
const SLONIK_LOG_NORMALISED = (0, _boolean.default)(process.env.SLONIK_LOG_NORMALISED);
exports.SLONIK_LOG_NORMALISED = SLONIK_LOG_NORMALISED;
const SLONIK_LOG_STACK_TRACE = (0, _boolean.default)(process.env.SLONIK_LOG_STACK_TRACE);

@@ -18,0 +16,0 @@ exports.SLONIK_LOG_STACK_TRACE = SLONIK_LOG_STACK_TRACE;

@@ -8,8 +8,4 @@ "use strict";

var _prettyHrtime = _interopRequireDefault(require("pretty-hrtime"));
var _serializeError = _interopRequireDefault(require("serialize-error"));
var _getStackTrace = require("get-stack-trace");
var _utilities = require("../utilities");

@@ -19,96 +15,48 @@

var _config = require("../config");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const stringifyCallSite = callSite => {
return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
}; // eslint-disable-next-line complexity
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const query = async (log, connection, clientConfiguration, rawSql, values, inheritedQueryId) => {
const query = async (connectionLogger, connection, clientConfiguration, rawSql, values, inheritedQueryId) => {
const queryId = inheritedQueryId || (0, _utilities.createQueryId)();
let stackTrace;
const log = connectionLogger.child({
queryId
});
const originalQuery = {
sql: rawSql,
values
};
if (_config.SLONIK_LOG_STACK_TRACE) {
const callSites = await (0, _getStackTrace.getStackTrace)();
stackTrace = callSites.map(callSite => {
return stringifyCallSite(callSite);
});
}
let actualQuery = _objectSpread({}, originalQuery);
const strippedSql = (0, _utilities.stripComments)(rawSql);
let rowCount = null;
let normalized;
const start = process.hrtime();
const interceptors = clientConfiguration && clientConfiguration.interceptors || [];
log.info('test');
const executionContext = {
log,
originalQuery,
queryId,
sharedContext: {}
};
try {
let result;
for (const interceptor of interceptors) {
if (interceptor.beforeQuery) {
const maybeResult = await interceptor.beforeQuery({
sql: rawSql,
values
});
if (maybeResult) {
return maybeResult;
}
}
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.transformQuery) {
actualQuery = await interceptor.transformQuery(executionContext, actualQuery);
}
}
if (Array.isArray(values)) {
normalized = (0, _utilities.normalizeAnonymousValuePlaceholders)(strippedSql, values);
} else if (values) {
normalized = (0, _utilities.normalizeNamedValuePlaceholders)(strippedSql, values);
} // eslint-disable-next-line flowtype/no-weak-types
let result;
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.beforeQueryExecution) {
result = await interceptor.beforeQueryExecution(executionContext, actualQuery);
const payload = {
queryId,
rowCount,
sql: strippedSql
};
if (_config.SLONIK_LOG_STACK_TRACE) {
payload.stackTrace = stackTrace;
}
if (_config.SLONIK_LOG_VALUES) {
payload.values = values;
}
if (_config.SLONIK_LOG_NORMALISED) {
payload.normalized = normalized;
}
log.debug(payload, 'executing query');
if (normalized) {
result = connection.query(normalized.sql, normalized.values);
} else {
result = connection.query(strippedSql);
}
result = await result;
for (const interceptor of interceptors) {
if (interceptor.afterQuery) {
result = await interceptor.afterQuery({
sql: rawSql,
values
}, result);
if (result) {
return result;
}
} // @todo Use rowCount only if the query is UPDATE/ INSERT.
if (result.rowCount) {
rowCount = result.rowCount;
} else if (Array.isArray(result)) {
rowCount = result.length;
}
}
return result;
try {
result = await connection.query(actualQuery.sql, actualQuery.values);
} catch (error) {

@@ -137,12 +85,11 @@ log.error({

throw error;
} finally {
const end = process.hrtime(start); // eslint-disable-next-line flowtype/no-weak-types
}
const payload = {
executionTime: (0, _prettyHrtime.default)(end),
queryId,
rowCount
};
log.debug(payload, 'query execution result');
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.afterQueryExecution) {
result = await interceptor.afterQueryExecution(executionContext, actualQuery, result);
}
}
return result;
};

@@ -149,0 +96,0 @@

@@ -20,8 +20,9 @@ "use strict";

var _createClientConfiguration = _interopRequireDefault(require("./createClientConfiguration"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// @see https://github.com/facebook/flow/issues/2977#issuecomment-390613203
const defaultClientConfiguration = Object.freeze({});
const createPool = (connectionConfiguration, clientUserConfiguration) => {
const clientConfiguration = (0, _createClientConfiguration.default)(clientUserConfiguration);
const createPool = (connectionConfiguration, clientConfiguration = defaultClientConfiguration) => {
const poolLog = _Logger.default.child({

@@ -28,0 +29,0 @@ poolId: (0, _utilities.createUlid)()

@@ -6,8 +6,2 @@ "use strict";

});
Object.defineProperty(exports, "createConnection", {
enumerable: true,
get: function () {
return _createConnection.default;
}
});
Object.defineProperty(exports, "createPool", {

@@ -20,4 +14,2 @@ enumerable: true,

var _createConnection = _interopRequireDefault(require("./createConnection"));
var _createPool = _interopRequireDefault(require("./createPool"));

@@ -24,0 +16,0 @@

@@ -6,18 +6,18 @@ "use strict";

});
Object.defineProperty(exports, "createConnection", {
Object.defineProperty(exports, "createPool", {
enumerable: true,
get: function () {
return _factories.createConnection;
return _factories.createPool;
}
});
Object.defineProperty(exports, "createPool", {
Object.defineProperty(exports, "createFieldNameTransformationInterceptor", {
enumerable: true,
get: function () {
return _factories.createPool;
return _interceptors.createFieldNameTransformationInterceptor;
}
});
Object.defineProperty(exports, "createFormatFieldNameInterceptor", {
Object.defineProperty(exports, "createQueryNormalizationInterceptor", {
enumerable: true,
get: function () {
return _interceptors.createFormatFieldNameInterceptor;
return _interceptors.createQueryNormalizationInterceptor;
}

@@ -24,0 +24,0 @@ });

@@ -6,12 +6,28 @@ "use strict";

});
Object.defineProperty(exports, "createFormatFieldNameInterceptor", {
Object.defineProperty(exports, "createFieldNameTransformationInterceptor", {
enumerable: true,
get: function () {
return _createFormatFieldNameInterceptor.default;
return _createFieldNameTransformationInterceptor.default;
}
});
Object.defineProperty(exports, "createLogInterceptor", {
enumerable: true,
get: function () {
return _createLogInterceptor.default;
}
});
Object.defineProperty(exports, "createQueryNormalizationInterceptor", {
enumerable: true,
get: function () {
return _createQueryNormalizationInterceptor.default;
}
});
var _createFormatFieldNameInterceptor = _interopRequireDefault(require("./createFormatFieldNameInterceptor"));
var _createFieldNameTransformationInterceptor = _interopRequireDefault(require("./createFieldNameTransformationInterceptor"));
var _createLogInterceptor = _interopRequireDefault(require("./createLogInterceptor"));
var _createQueryNormalizationInterceptor = _interopRequireDefault(require("./createQueryNormalizationInterceptor"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
//# sourceMappingURL=index.js.map

@@ -23,4 +23,4 @@ "use strict";

if (value && value.type === 'RAW' && typeof value.raw === 'string') {
raw += value.raw;
if (value && value.type === 'RAW_SQL' && typeof value.sql === 'string') {
raw += value.sql;
} else if (value && value.type === 'IDENTIFIER' && Array.isArray(value.names)) {

@@ -56,6 +56,6 @@ raw += value.names.map(identifierName => {

sql.raw = raw => {
sql.raw = rawSql => {
return {
raw,
type: 'RAW'
sql: rawSql,
type: 'RAW_SQL'
};

@@ -62,0 +62,0 @@ };

@@ -11,3 +11,3 @@ "use strict";

const createUlid = () => {
return (0, _ulid.factory)((0, _ulid.detectPrng)(true));
return (0, _ulid.factory)((0, _ulid.detectPrng)(true))();
};

@@ -14,0 +14,0 @@

@@ -42,14 +42,2 @@ "use strict";

});
Object.defineProperty(exports, "normalizeAnonymousValuePlaceholders", {
enumerable: true,
get: function () {
return _normalizeAnonymousValuePlaceholders.default;
}
});
Object.defineProperty(exports, "normalizeNamedValuePlaceholders", {
enumerable: true,
get: function () {
return _normalizeNamedValuePlaceholders.default;
}
});
Object.defineProperty(exports, "stripComments", {

@@ -74,6 +62,2 @@ enumerable: true,

var _normalizeAnonymousValuePlaceholders = _interopRequireDefault(require("./normalizeAnonymousValuePlaceholders"));
var _normalizeNamedValuePlaceholders = _interopRequireDefault(require("./normalizeNamedValuePlaceholders"));
var _stripComments = _interopRequireDefault(require("./stripComments"));

@@ -80,0 +64,0 @@

@@ -95,3 +95,3 @@ {

},
"version": "11.2.0"
"version": "12.0.0"
}

@@ -17,3 +17,2 @@ <a name="slonik"></a>

* [Convenience methods](#slonik-query-methods) with built-in assertions.
* Anonymous, named and tagged template literal [value placeholders](#slonik-value-placeholders).
* [Middleware](#slonik-interceptors) support.

@@ -40,11 +39,9 @@ * [Syntax highlighting](#slonik-syntax-highlighting) (Atom plugin compatible with Slonik).

* [Usage](#slonik-usage)
* [Configuration](#slonik-usage-configuration)
* [API](#slonik-usage-api)
* [Checking out a client from the connection pool](#slonik-usage-checking-out-a-client-from-the-connection-pool)
* [Interceptors](#slonik-interceptors)
* [`beforeQuery`](#slonik-interceptors-beforequery)
* [`beforeConnectionEnd`](#slonik-interceptors-beforeconnectionend)
* [`beforePoolConnectionRelease`](#slonik-interceptors-beforepoolconnectionrelease)
* [`afterQuery`](#slonik-interceptors-afterquery)
* [Interceptor methods](#slonik-interceptors-interceptor-methods)
* [Built-in interceptors](#slonik-built-in-interceptors)
* [Field name formatter](#slonik-built-in-interceptors-field-name-formatter)
* [Field name transformation interceptor](#slonik-built-in-interceptors-field-name-transformation-interceptor)
* [Query normalization interceptor](#slonik-built-in-interceptors-query-normalization-interceptor)
* [Recipes](#slonik-recipes)

@@ -56,7 +53,5 @@ * [Logging `auto_explain`](#slonik-recipes-logging-auto_explain)

* [Value placeholders](#slonik-value-placeholders)
* [Anonymous placeholders](#slonik-value-placeholders-anonymous-placeholders)
* [A value set](#slonik-value-placeholders-a-value-set)
* [Tagged template literals](#slonik-value-placeholders-tagged-template-literals)
* [Sets](#slonik-value-placeholders-sets)
* [Multiple value sets](#slonik-value-placeholders-multiple-value-sets)
* [Named placeholders](#slonik-value-placeholders-named-placeholders)
* [Tagged template literals](#slonik-value-placeholders-tagged-template-literals)
* [Query methods](#slonik-query-methods)

@@ -92,20 +87,41 @@ * [`any`](#slonik-query-methods-any)

Slonik exports two factory functions:
Use `createPool` to create a connection pool, e.g.
* `createPool`
* `createConnection`
```js
import {
createPool
} from 'slonik';
The API of the query method is equivalent to that of [`pg`](https://travis-ci.org/brianc/node-postgres).
const pool = createPool('postgres://');
Refer to [query methods](#slonik-query-methods) for documentation of Slonik-specific query methods.
```
<a name="slonik-usage-configuration"></a>
### Configuration
Instance of Slonik connection pool can be then used to create a new connection, e.g.
Both functions accept the same parameters:
```js
pool.connect(async (connection) => {
await connection.query(sql`SELECT 1`);
});
* `connectionConfiguration`
* `clientConfiguration`
```
The connection will be kept alive until the promise resolves (the result of the method supplied to `connect()`).
Refer to [query method](#slonik-query-methods) documentation to learn about the connection methods.
If you do not require having a persistent connection to the same backend, then you can directly use `pool` to run queries, e.g.
```js
pool.query(sql`SELECT 1`);
```
Beware that in the latter example, the connection picked to execute the query is a random connection from the connection pool, i.e. using the latter method (without explicit `connect()`) does not guarantee that multiple queries will refer to the same backend.
<a name="slonik-usage-api"></a>
### API
```js
createPool(connectionConfiguration: DatabaseConfigurationType, clientConfiguration: ClientConfigurationType): DatabasePoolType;
type DatabaseConnectionUriType = string;

@@ -127,7 +143,5 @@

* @property interceptors An array of [Slonik interceptors](https://github.com/gajus/slonik#slonik-interceptors).
* @property onConnect A new connection handler. Executed after a connection is established, but before allowing the connection to be used by any clients.
*/
type ClientConfigurationType = {|
+interceptors?: $ReadOnlyArray<InterceptorType>,
+onConnect?: (connection: DatabaseConnectionType) => MaybePromiseType<void>
+interceptors?: $ReadOnlyArray<InterceptorType>
|};

@@ -153,3 +167,3 @@

Slonik only allows to check out a connection for a duration of promise routine supplied to the `connect()` method.
Slonik only allows to check out a connection for the duration of the promise routine supplied to the `connect()` method.

@@ -225,6 +239,17 @@ ```js

type InterceptorType = {|
+afterQuery?: (query: QueryType, result: QueryResultType<QueryResultRowType>) => MaybePromiseType<QueryResultType<QueryResultRowType>>,
+beforeConnectionEnd?: (connection: DatabaseSingleConnectionType) => MaybePromiseType<void>,
+afterPoolConnection?: (connection: DatabasePoolConnectionType) => MaybePromiseType<void>,
+afterQueryExecution?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType,
result: QueryResultType<QueryResultRowType>
) => MaybePromiseType<QueryResultType<QueryResultRowType>>,
+beforePoolConnectionRelease?: (connection: DatabasePoolConnectionType) => MaybePromiseType<void>,
+beforeQuery?: (query: QueryType) => Promise<QueryResultType<QueryResultRowType>> | QueryResultType<QueryResultRowType> | MaybePromiseType<void>
+beforeQueryExecution?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType
) => MaybePromiseType<QueryResultType<QueryResultRowType>> | MaybePromiseType<void>,
+transformQuery?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType
) => MaybePromiseType<QueryType>
|};

@@ -249,36 +274,37 @@

There are 2 functions that an interceptor can implement:
* `beforeQuery`
* `beforeConnectionEnd`
* `beforePoolConnectionRelease`
* `afterQuery`
Interceptors are executed in the order they are added.
<a name="slonik-interceptors-beforequery"></a>
### <code>beforeQuery</code>
<a name="slonik-interceptors-interceptor-methods"></a>
### Interceptor methods
`beforeQuery` is the first interceptor function executed in the query execution cycle.
<a name="slonik-interceptors-interceptor-methods-afterpoolconnection"></a>
#### <code>afterPoolConnection</code>
This function can optionally return a direct result of the query which will cause the actual query never to be executed.
Executed after a connection is , e.g.
<a name="slonik-interceptors-beforeconnectionend"></a>
### <code>beforeConnectionEnd</code>
`beforeConnectionEnd` is executed before a connection is explicitly ended, e.g.
```js
const connection = await createConnection('postgres://');
const pool = createPool('postgres://');
// Interceptor is executed here. ↓
connection.end();
pool.connect();
```
<a name="slonik-interceptors-beforepoolconnectionrelease"></a>
### <code>beforePoolConnectionRelease</code>
<a name="slonik-interceptors-interceptor-methods-afterqueryexecution"></a>
#### <code>afterQueryExecution</code>
`beforePoolConnectionRelease` is executed before connection is released back to the connection pool, e.g.
`afterQueryExecution` must return the result of the query, which will be passed down to the client.
Use `afterQuery` to modify the query result.
<a name="slonik-interceptors-interceptor-methods-beforequeryexecution"></a>
#### <code>beforeQueryExecution</code>
This function can optionally return a direct result of the query which will cause the actual query never to be executed.
<a name="slonik-interceptors-interceptor-methods-beforepoolconnectionrelease"></a>
#### <code>beforePoolConnectionRelease</code>
Executed before connection is released back to the connection pool, e.g.
```js

@@ -295,16 +321,14 @@ const pool = await createPool('postgres://');

<a name="slonik-interceptors-afterquery"></a>
### <code>afterQuery</code>
<a name="slonik-interceptors-interceptor-methods-transformquery"></a>
#### <code>transformQuery</code>
`afterQuery` is the last interceptor function executed in the query execution cycle.
Executed before `beforeQueryExecution`.
This function must return the result of the query, which will be passed down to the client.
Transforms query.
Use `afterQuery` to modify the query result.
<a name="slonik-built-in-interceptors"></a>
## Built-in interceptors
<a name="slonik-built-in-interceptors-field-name-formatter"></a>
### Field name formatter
<a name="slonik-built-in-interceptors-field-name-transformation-interceptor"></a>
### Field name transformation interceptor

@@ -327,3 +351,3 @@ `createFormatFieldNameInterceptor` creates an interceptor that formats query result field names.

<a name="slonik-built-in-interceptors-field-name-formatter-api"></a>
<a name="slonik-built-in-interceptors-field-name-transformation-interceptor-api-1"></a>
#### API

@@ -345,3 +369,3 @@

<a name="slonik-built-in-interceptors-field-name-formatter-example-usage"></a>
<a name="slonik-built-in-interceptors-field-name-transformation-interceptor-example-usage"></a>
#### Example usage

@@ -351,3 +375,3 @@

import {
createFormatFieldNameInterceptor,
createFieldNameTransformationInterceptor,
createPool

@@ -357,3 +381,3 @@ } from 'slonik';

const interceptors = [
createFormatFieldNameInterceptor({
createFieldNameTransformationInterceptor({
format: 'CAMEL_CASE'

@@ -383,3 +407,23 @@ })

<a name="slonik-built-in-interceptors-query-normalization-interceptor"></a>
### Query normalization interceptor
Normalizes the query.
<a name="slonik-built-in-interceptors-query-normalization-interceptor-api-2"></a>
#### API
```js
/**
* @property stripComments Strips comments from the query (default: true).
*/
type ConfigurationType = {|
+stripComments?: boolean
|};
(configuration?: ConfigurationType) => InterceptorType;
```
<a name="slonik-recipes"></a>

@@ -412,14 +456,20 @@ ## Recipes

This can be configured using `onConnect` connection handler.
This can be configured using `afterPoolConnection` interceptor, e.g.
```js
const pool = await createPool('postgres://localhost', {
onConnect: async (connection) => {
await connection.query(sql`LOAD 'auto_explain'`);
await connection.query(sql`SET auto_explain.log_analyze=true`);
await connection.query(sql`SET auto_explain.log_format=json`);
await connection.query(sql`SET auto_explain.log_min_duration=0`);
await connection.query(sql`SET auto_explain.log_timing=true`);
await connection.query(sql`SET client_min_messages=log`);
}
interceptors: [
{
afterPoolConnection: async (connection) => {
await connection.query(sql`LOAD 'auto_explain'`);
await connection.query(sql`SET auto_explain.log_analyze=true`);
await connection.query(sql`SET auto_explain.log_format=json`);
await connection.query(sql`SET auto_explain.log_min_duration=0`);
await connection.query(sql`SET auto_explain.log_timing=true`);
await connection.query(sql`SET client_min_messages=log`);
}
}
]
});

@@ -503,36 +553,28 @@

<a name="slonik-value-placeholders-anonymous-placeholders"></a>
### Anonymous placeholders
<a name="slonik-value-placeholders-tagged-template-literals"></a>
### Tagged template literals
Slonik enables use of question mark (`?`) value placeholders, e.g.
Slonik query methods can only be executed using `sql` [tagged template literal](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals), e.g.
```js
await connection.query(sql`SELECT ?`, [
1
]);
import {
sql
} from 'slonik'
```
connection.query(sql`
INSERT INTO reservation_ticket (reservation_id, ticket_id)
VALUES ${values}
`);
Question mark value placeholders are converted to positional value placeholders before they are passed to the `pg` driver, i.e. the above query becomes:
```sql
SELECT $1
```
Note: Mixing anonymous and position placeholders in a single query will result in an error.
<a name="slonik-value-placeholders-sets"></a>
### Sets
<a name="slonik-value-placeholders-a-value-set"></a>
### A value set
Array expressions produce sets, e.g.
A question mark is interpolated into a value set when the associated value is an array, e.g.
```js
await connection.query(sql`SELECT ?`, [
[
1,
2,
3
]
]);
await connection.query(sql`
SELECT ${[1, 2, 3]}
`);

@@ -551,19 +593,11 @@ ```

A question mark is interpolated into a list of value sets when the associated value is an array of arrays, e.g.
An array containing array expressions produce a collection of sets, e.g.
```js
await connection.query(sql`SELECT ?`, [
[
[
1,
2,
3
],
[
1,
2,
3
]
]
]);
await connection.query(sql`
SELECT ${[
[1, 2, 3],
[1, 2, 3]
]}
`);

@@ -579,51 +613,3 @@ ```

<a name="slonik-value-placeholders-named-placeholders"></a>
### Named placeholders
A `:[a-zA-Z]` regex is used to match named placeholders.
```js
await connection.query(sql`SELECT :foo`, {
foo: 'FOO'
});
```
Produces:
```sql
SELECT $1
```
<a name="slonik-value-placeholders-tagged-template-literals"></a>
### Tagged template literals
Slonik query methods can only be executed using `sql` [tagged template literal](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals), e.g.
```js
import {
sql
} from 'slonik'
connection.query(sql`
INSERT INTO reservation_ticket (reservation_id, ticket_id)
VALUES ${values}
`);
```
Arguments of a tagged template literal invocation are replaced with an anonymous value placeholder, i.e. the latter query is equivalent to:
```js
connection.query(sql`
INSERT INTO reservation_ticket (reservation_id, ticket_id)
VALUES ?
`, [
values
]);
```
<a name="slonik-value-placeholders-tagged-template-literals-creating-dynamic-delimited-identifiers"></a>
<a name="slonik-value-placeholders-multiple-value-sets-creating-dynamic-delimited-identifiers"></a>
#### Creating dynamic delimited identifiers

@@ -648,3 +634,3 @@

<a name="slonik-value-placeholders-tagged-template-literals-inlining-dynamic-raw-sql"></a>
<a name="slonik-value-placeholders-multiple-value-sets-inlining-dynamic-raw-sql"></a>
#### Inlining dynamic/ raw SQL

@@ -994,5 +980,2 @@

# Logs normalised query and input values
export SLONIK_LOG_NORMALISED=true
```

@@ -999,0 +982,0 @@

@@ -40,4 +40,6 @@ // @flow

if (clientConfiguration.onConnect) {
await clientConfiguration.onConnect(boundConnection);
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.afterPoolConnection) {
await interceptor.afterPoolConnection(boundConnection);
}
}

@@ -50,5 +52,3 @@

} finally {
const interceptors = clientConfiguration && clientConfiguration.interceptors || [];
for (const interceptor of interceptors) {
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.beforePoolConnectionRelease) {

@@ -55,0 +55,0 @@ await interceptor.beforePoolConnectionRelease(boundConnection);

@@ -5,3 +5,2 @@ // @flow

export {default as bindPoolConnection} from './bindPoolConnection';
export {default as bindSingleConnection} from './bindSingleConnection';
export {default as bindTransactionConnection} from './bindTransactionConnection';

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

export const SLONIK_LOG_NORMALISED = boolean(process.env.SLONIK_LOG_NORMALISED);
export const SLONIK_LOG_STACK_TRACE = boolean(process.env.SLONIK_LOG_STACK_TRACE);
export const SLONIK_LOG_VALUES = boolean(process.env.SLONIK_LOG_VALUES);
// @flow
import prettyHrtime from 'pretty-hrtime';
import serializeError from 'serialize-error';
import {
getStackTrace
} from 'get-stack-trace';
import {
createQueryId,
normalizeAnonymousValuePlaceholders,
normalizeNamedValuePlaceholders,
stripComments
createQueryId
} from '../utilities';

@@ -20,108 +13,52 @@ import {

} from '../errors';
import {
SLONIK_LOG_NORMALISED,
SLONIK_LOG_STACK_TRACE,
SLONIK_LOG_VALUES
} from '../config';
import type {
InternalQueryFunctionType
InternalQueryFunctionType,
QueryExecutionContextType
} from '../types';
const stringifyCallSite = (callSite) => {
return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
};
// eslint-disable-next-line complexity
const query: InternalQueryFunctionType<*> = async (log, connection, clientConfiguration, rawSql, values, inheritedQueryId) => {
const query: InternalQueryFunctionType<*> = async (connectionLogger, connection, clientConfiguration, rawSql, values, inheritedQueryId) => {
const queryId = inheritedQueryId || createQueryId();
let stackTrace;
const log = connectionLogger.child({
queryId
});
if (SLONIK_LOG_STACK_TRACE) {
const callSites = await getStackTrace();
const originalQuery = {
sql: rawSql,
values
};
stackTrace = callSites
.map((callSite) => {
return stringifyCallSite(callSite);
});
}
let actualQuery = {
...originalQuery
};
const strippedSql = stripComments(rawSql);
log.info('test');
let rowCount: number | null = null;
const executionContext: QueryExecutionContextType = {
log,
originalQuery,
queryId,
sharedContext: {}
};
let normalized;
const start = process.hrtime();
const interceptors = clientConfiguration && clientConfiguration.interceptors || [];
try {
let result;
for (const interceptor of interceptors) {
if (interceptor.beforeQuery) {
const maybeResult = await interceptor.beforeQuery({
sql: rawSql,
values
});
if (maybeResult) {
return maybeResult;
}
}
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.transformQuery) {
actualQuery = await interceptor.transformQuery(executionContext, actualQuery);
}
}
if (Array.isArray(values)) {
normalized = normalizeAnonymousValuePlaceholders(strippedSql, values);
} else if (values) {
normalized = normalizeNamedValuePlaceholders(strippedSql, values);
}
let result;
// eslint-disable-next-line flowtype/no-weak-types
const payload: Object = {
queryId,
rowCount,
sql: strippedSql
};
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.beforeQueryExecution) {
result = await interceptor.beforeQueryExecution(executionContext, actualQuery);
if (SLONIK_LOG_STACK_TRACE) {
payload.stackTrace = stackTrace;
}
if (SLONIK_LOG_VALUES) {
payload.values = values;
}
if (SLONIK_LOG_NORMALISED) {
payload.normalized = normalized;
}
log.debug(payload, 'executing query');
if (normalized) {
result = connection.query(normalized.sql, normalized.values);
} else {
result = connection.query(strippedSql);
}
result = await result;
for (const interceptor of interceptors) {
if (interceptor.afterQuery) {
result = await interceptor.afterQuery({
sql: rawSql,
values
}, result);
if (result) {
return result;
}
}
}
// @todo Use rowCount only if the query is UPDATE/ INSERT.
if (result.rowCount) {
rowCount = result.rowCount;
} else if (Array.isArray(result)) {
rowCount = result.length;
}
return result;
try {
result = await connection.query(actualQuery.sql, actualQuery.values);
} catch (error) {

@@ -150,16 +87,13 @@ log.error({

throw error;
} finally {
const end = process.hrtime(start);
}
// eslint-disable-next-line flowtype/no-weak-types
const payload: Object = {
executionTime: prettyHrtime(end),
queryId,
rowCount
};
for (const interceptor of clientConfiguration.interceptors) {
if (interceptor.afterQueryExecution) {
result = await interceptor.afterQueryExecution(executionContext, actualQuery, result);
}
}
log.debug(payload, 'query execution result');
}
return result;
};
export default query;

@@ -13,3 +13,3 @@ // @flow

import type {
ClientConfigurationType,
ClientUserConfigurationType,
DatabasePoolType,

@@ -20,10 +20,10 @@ DatabaseConfigurationType

import bindPool from '../binders/bindPool';
import createClientConfiguration from './createClientConfiguration';
// @see https://github.com/facebook/flow/issues/2977#issuecomment-390613203
const defaultClientConfiguration = Object.freeze({});
export default (
connectionConfiguration: DatabaseConfigurationType,
clientConfiguration: ClientConfigurationType = defaultClientConfiguration
clientUserConfiguration?: ClientUserConfigurationType
): DatabasePoolType => {
const clientConfiguration = createClientConfiguration(clientUserConfiguration);
const poolLog = Logger.child({

@@ -30,0 +30,0 @@ poolId: createUlid()

// @flow
export {default as createConnection} from './createConnection';
export {default as createPool} from './createPool';

@@ -28,3 +28,2 @@ // @flow

DatabasePoolType,
DatabaseSingleConnectionType,
DatabaseTransactionConnectionType,

@@ -34,7 +33,7 @@ InterceptorType

export {
createConnection,
createPool
} from './factories';
export {
createFormatFieldNameInterceptor
createFieldNameTransformationInterceptor,
createQueryNormalizationInterceptor
} from './interceptors';

@@ -41,0 +40,0 @@ export {

// @flow
export {default as createFormatFieldNameInterceptor} from './createFormatFieldNameInterceptor';
export {default as createFieldNameTransformationInterceptor} from './createFieldNameTransformationInterceptor';
export {default as createLogInterceptor} from './createLogInterceptor';
export {default as createQueryNormalizationInterceptor} from './createQueryNormalizationInterceptor';

@@ -5,4 +5,4 @@ // @flow

AnonymouseValuePlaceholderValueType,
QueryIdentifierType,
RawQueryType,
IdentifierTokenType,
RawSqlTokenType,
TaggledTemplateLiteralInvocationType

@@ -30,4 +30,4 @@ } from '../types';

if (value && value.type === 'RAW' && typeof value.raw === 'string') {
raw += value.raw;
if (value && value.type === 'RAW_SQL' && typeof value.sql === 'string') {
raw += value.sql;
} else if (value && value.type === 'IDENTIFIER' && Array.isArray(value.names)) {

@@ -58,3 +58,3 @@ raw += value.names

sql.identifier = (names: $ReadOnlyArray<string>): QueryIdentifierType => {
sql.identifier = (names: $ReadOnlyArray<string>): IdentifierTokenType => {
// @todo Replace `type` with a symbol once Flow adds symbol support

@@ -68,6 +68,6 @@ // @see https://github.com/facebook/flow/issues/810

sql.raw = (raw: string): RawQueryType => {
sql.raw = (rawSql: string): RawSqlTokenType => {
return {
raw,
type: 'RAW'
sql: rawSql,
type: 'RAW_SQL'
};

@@ -74,0 +74,0 @@ };

@@ -44,7 +44,9 @@ // @flow

* @property interceptors An array of [Slonik interceptors](https://github.com/gajus/slonik#slonik-interceptors).
* @property onConnect A new connection handler. Executed after a connection is established, but before allowing the connection to be used by any clients.
*/
export type ClientUserConfigurationType = {|
+interceptors?: $ReadOnlyArray<InterceptorType>
|};
export type ClientConfigurationType = {|
+interceptors?: $ReadOnlyArray<InterceptorType>,
+onConnect?: (connection: DatabaseConnectionType) => MaybePromiseType<void>
+interceptors: $ReadOnlyArray<InterceptorType>
|};

@@ -84,8 +86,2 @@

export type DatabaseSingleConnectionType = {|
...CommonQueryMethodsType,
+end: () => Promise<void>,
+transaction: (handler: TransactionFunctionType) => Promise<*>
|};
export type DatabasePoolConnectionType = {|

@@ -111,4 +107,3 @@ ...CommonQueryMethodsType,

...DatabasePoolConnectionType,
...DatabasePoolType,
...DatabaseSingleConnectionType
...DatabasePoolType
}>;

@@ -127,8 +122,17 @@

export type NormalizedQueryType = {|
+sql: string,
+values: $ReadOnlyArray<*>
/**
* @property log Instance of Roarr logger with query execution context parameters.
* @property originalQuery A copy of the query before `transformQuery` middleware.
* @property queryId Unique query identifier.
* @property sharedContext A context shared between all interceptors. Use this to share information between interceptors.
*/
export type QueryExecutionContextType = {|
+log: LoggerType,
+originalQuery: QueryType,
+queryId: QueryIdType,
// eslint-disable-next-line flowtype/no-weak-types
+sharedContext: Object
|};
export type QueryIdentifierType = {|
export type IdentifierTokenType = {|
names: $ReadOnlyArray<string>,

@@ -138,5 +142,5 @@ type: 'IDENTIFIER'

export type RawQueryType = {|
raw: string,
type: 'RAW'
export type RawSqlTokenType = {|
sql: string,
type: 'RAW_SQL'
|};

@@ -152,4 +156,4 @@

QueryPrimitiveValueType |
QueryIdentifierType |
RawQueryType;
IdentifierTokenType |
RawSqlTokenType;

@@ -165,4 +169,4 @@ export type NamedValuePlaceholderValuesType = {

export type TaggledTemplateLiteralInvocationType = {|
sql: string,
values: $ReadOnlyArray<AnonymouseValuePlaceholderValueType>
+sql: string,
+values: $ReadOnlyArray<AnonymouseValuePlaceholderValueType>
|};

@@ -176,3 +180,3 @@

values?: DatabaseQueryValuesType,
queryId?: QueryIdType
uid?: QueryIdType
) => Promise<R>;

@@ -213,6 +217,17 @@

export type InterceptorType = {|
+afterQuery?: (query: QueryType, result: QueryResultType<QueryResultRowType>) => MaybePromiseType<QueryResultType<QueryResultRowType>>,
+beforeConnectionEnd?: (connection: DatabaseSingleConnectionType) => MaybePromiseType<void>,
+afterPoolConnection?: (connection: DatabasePoolConnectionType) => MaybePromiseType<void>,
+afterQueryExecution?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType,
result: QueryResultType<QueryResultRowType>
) => MaybePromiseType<QueryResultType<QueryResultRowType>>,
+beforePoolConnectionRelease?: (connection: DatabasePoolConnectionType) => MaybePromiseType<void>,
+beforeQuery?: (query: QueryType) => Promise<QueryResultType<QueryResultRowType>> | QueryResultType<QueryResultRowType> | MaybePromiseType<void>
+beforeQueryExecution?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType
) => MaybePromiseType<QueryResultType<QueryResultRowType>> | MaybePromiseType<void>,
+transformQuery?: (
queryExecutionContext: QueryExecutionContextType,
query: QueryType
) => MaybePromiseType<QueryType>
|};

@@ -9,3 +9,3 @@ // @flow

export default (): string => {
return ulidFactory(detectPrng(true));
return ulidFactory(detectPrng(true))();
};

@@ -9,4 +9,2 @@ // @flow

export {default as mapTaggedTemplateLiteralInvocation} from './mapTaggedTemplateLiteralInvocation';
export {default as normalizeAnonymousValuePlaceholders} from './normalizeAnonymousValuePlaceholders';
export {default as normalizeNamedValuePlaceholders} from './normalizeNamedValuePlaceholders';
export {default as stripComments} from './stripComments';

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

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

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

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

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

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

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

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

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc