Comparing version 13.1.1 to 14.0.0
@@ -44,3 +44,3 @@ "use strict"; | ||
for (const fragmentValue of fragmentValues) { | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(fragmentValue), 'Unexpected set member type.'); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(fragmentValue), 'Unexpected value binding type.'); | ||
bindings.push(fragmentValue); | ||
@@ -60,38 +60,49 @@ } | ||
continue; | ||
} else if (value && value.type === 'SET' && Array.isArray(value.members)) { | ||
} else if (value && value.type === 'VALUE_LIST' && Array.isArray(value.values)) { | ||
const placeholders = []; | ||
let placeholderIndex = bindings.length; | ||
for (const member of value.members) { | ||
for (const listValue of value.values) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(member), 'Unexpected set member type.'); | ||
bindings.push(member); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(listValue), 'Unexpected value list member type.'); | ||
bindings.push(listValue); | ||
} | ||
raw += placeholders.join(', '); | ||
} else if (value && value.type === 'TUPLE' && Array.isArray(value.values)) { | ||
const placeholders = []; | ||
let placeholderIndex = bindings.length; | ||
for (const tupleValue of value.values) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(tupleValue), 'Unexpected tuple member type.'); | ||
bindings.push(tupleValue); | ||
} | ||
raw += '(' + placeholders.join(', ') + ')'; | ||
} else if (value && value.type === 'MULTISET' && Array.isArray(value.sets)) { | ||
} else if (value && value.type === 'TUPLE_LIST' && Array.isArray(value.tuples)) { | ||
let placeholderIndex = bindings.length; | ||
const multisetMemberSql = []; | ||
let lastSetSize; | ||
const tupleListMemberSql = []; | ||
let lastTupleSize; | ||
for (const set of value.sets) { | ||
for (const tuple of value.tuples) { | ||
const placeholders = []; | ||
(0, _invariant.default)(Array.isArray(set), 'Unexpected set shape.'); | ||
(0, _invariant.default)(Array.isArray(tuple), 'Unexpected tuple 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.'); | ||
if (typeof lastTupleSize === 'number' && lastTupleSize !== tuple.length) { | ||
throw new Error('Each tuple in a list of tuples must have an equal number of members.'); | ||
} | ||
lastSetSize = set.length; | ||
lastTupleSize = tuple.length; | ||
for (const member of set) { | ||
for (const member of tuple) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(member), 'Unexpected set member type.'); | ||
(0, _invariant.default)((0, _isPrimitiveValueExpression.default)(member), 'Unexpected tuple member type.'); | ||
bindings.push(member); | ||
} | ||
multisetMemberSql.push('(' + placeholders.join(', ') + ')'); | ||
tupleListMemberSql.push('(' + placeholders.join(', ') + ')'); | ||
} | ||
raw += multisetMemberSql.join(', '); | ||
raw += tupleListMemberSql.join(', '); | ||
} else if ((0, _isPrimitiveValueExpression.default)(value)) { | ||
@@ -132,18 +143,25 @@ raw += '$' + (bindings.length + 1); | ||
sql.set = members => { | ||
sql.valueList = values => { | ||
return { | ||
members, | ||
type: 'SET' | ||
type: 'VALUE_LIST', | ||
values | ||
}; | ||
}; | ||
sql.multiset = sets => { | ||
sql.tuple = values => { | ||
return { | ||
sets, | ||
type: 'MULTISET' | ||
type: 'TUPLE', | ||
values | ||
}; | ||
}; | ||
sql.tupleList = tuples => { | ||
return { | ||
tuples, | ||
type: 'TUPLE_LIST' | ||
}; | ||
}; | ||
var _default = sql; | ||
exports.default = _default; | ||
//# sourceMappingURL=sql.js.map |
@@ -96,3 +96,3 @@ { | ||
}, | ||
"version": "13.1.1" | ||
"version": "14.0.0" | ||
} |
@@ -18,3 +18,3 @@ <a name="slonik"></a> | ||
* [SQL injection guarding](#slonik-value-placeholders-tagged-template-literals). | ||
* [Set interpolation](#slonik-value-placeholders-sql-set). | ||
* [Value and tuple interpolation](#slonik-value-placeholders-sql-valuelist). | ||
* Detail [logging](#slonik-debugging). | ||
@@ -30,4 +30,18 @@ * [Parsing and logging of the auto_explain logs](#logging-auto_explain). | ||
--- | ||
<a name="slonik-battle-tested"></a> | ||
## Battle-Tested | ||
Slonik began as a collection of utilities designed for working with [`node-postgres`](https://github.com/brianc/node-postgres). We continue to use `node-postgres` as it provides a robust foundation for interacting with PostgreSQL. However, what once was a collection of utilities has since grown into a framework that abstracts repeating code patterns, protects against unsafe connection handling and value interpolation, and provides rich debugging experience. | ||
Slonik has been [battle-tested](https://medium.com/@gajus/lessons-learned-scaling-postgresql-database-to-1-2bn-records-month-edc5449b3067) with large data volumes and queries ranging from simple CRUD operations to data-warehousing needs. | ||
<a name="slonik-origin-of-the-name"></a> | ||
## Origin of the name | ||
![Slonik](./.README/postgresql-elephant.png) | ||
The name of the elephant depicted in the official PostgreSQL logo is Slonik. The name itself is derived from the Russian word for "little elephant". | ||
Read: [The History of Slonik, the PostgreSQL Elephant Logo](https://www.vertabelo.com/blog/notes-from-the-lab/the-history-of-slonik-the-postgresql-elephant-logo) | ||
<a name="slonik-documentation"></a> | ||
@@ -38,2 +52,4 @@ ## Documentation | ||
* [Features](#slonik-features) | ||
* [Battle-Tested](#slonik-battle-tested) | ||
* [Origin of the name](#slonik-origin-of-the-name) | ||
* [Documentation](#slonik-documentation) | ||
@@ -51,4 +67,2 @@ * [Usage](#slonik-usage) | ||
* [Using `sql.raw` to generate dynamic queries](#slonik-recipes-using-sql-raw-to-generate-dynamic-queries) | ||
* [Battle-Tested](#slonik-battle-tested) | ||
* [Origin of the name](#slonik-origin-of-the-name) | ||
* [Conventions](#slonik-conventions) | ||
@@ -58,4 +72,5 @@ * [No multiline values](#slonik-conventions-no-multiline-values) | ||
* [Tagged template literals](#slonik-value-placeholders-tagged-template-literals) | ||
* [`sql.set`](#slonik-value-placeholders-sql-set) | ||
* [`sql.multiset`](#slonik-value-placeholders-sql-multiset) | ||
* [`sql.valueList`](#slonik-value-placeholders-sql-valuelist) | ||
* [`sql.tuple`](#slonik-value-placeholders-sql-tuple) | ||
* [`sql.tupleList`](#slonik-value-placeholders-sql-tuplelist) | ||
* [`sql.identifier`](#slonik-value-placeholders-sql-identifier) | ||
@@ -574,18 +589,2 @@ * [`sql.raw`](#slonik-value-placeholders-sql-raw) | ||
<a name="slonik-battle-tested"></a> | ||
## Battle-Tested | ||
Slonik began as a collection of utilities designed for working with [`node-postgres`](https://github.com/brianc/node-postgres). We continue to use `node-postgres` as it provides a robust foundation for interacting with PostgreSQL. However, what once was a collection of utilities has since grown into a framework that abstracts repeating code patterns, protects against unsafe connection handling and value interpolation, and provides rich debugging experience. | ||
Slonik has been [battle-tested](https://medium.com/@gajus/lessons-learned-scaling-postgresql-database-to-1-2bn-records-month-edc5449b3067) with large data volumes and queries ranging from simple CRUD operations to data-warehousing needs. | ||
<a name="slonik-origin-of-the-name"></a> | ||
## Origin of the name | ||
![Slonik](./.README/postgresql-elephant.png) | ||
The name of the elephant depicted in the official PostgreSQL logo is Slonik. The name itself is derived from the Russian word for "little elephant". | ||
Read: [The History of Slonik, the PostgreSQL Elephant Logo](https://www.vertabelo.com/blog/notes-from-the-lab/the-history-of-slonik-the-postgresql-elephant-logo) | ||
<a name="slonik-conventions"></a> | ||
@@ -648,15 +647,15 @@ ## Conventions | ||
<a name="slonik-value-placeholders-sql-set"></a> | ||
### <code>sql.set</code> | ||
<a name="slonik-value-placeholders-sql-valuelist"></a> | ||
### <code>sql.valueList</code> | ||
```js | ||
(members: $ReadOnlyArray<PrimitiveValueExpressionType>) => SetSqlTokenType; | ||
(values: $ReadOnlyArray<PrimitiveValueExpressionType>) => ValueListSqlTokenType; | ||
``` | ||
`sql.set` is used to create a typed row construct (or a set, depending on the context), e.g. | ||
Creates a list of values, e.g. | ||
```js | ||
await connection.query(sql` | ||
SELECT ${sql.set([1, 2, 3])} | ||
SELECT (${sql.valueList([1, 2, 3])}) | ||
`); | ||
@@ -680,15 +679,48 @@ | ||
<a name="slonik-value-placeholders-sql-multiset"></a> | ||
### <code>sql.multiset</code> | ||
<a name="slonik-value-placeholders-sql-tuple"></a> | ||
### <code>sql.tuple</code> | ||
```js | ||
(sets: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>) => MultisetSqlTokenType; | ||
(values: $ReadOnlyArray<PrimitiveValueExpressionType>) => TupleSqlTokenType; | ||
``` | ||
`sql.multiset` is used to create a comma-separated list of typed row constructs, e.g. | ||
Creates a tuple (typed row construct), e.g. | ||
```js | ||
await connection.query(sql` | ||
SELECT ${sql.multiset([ | ||
INSERT INTO (foo, bar, baz) | ||
VALUES ${sql.tuple([1, 2, 3])} | ||
`); | ||
``` | ||
Produces: | ||
```js | ||
{ | ||
sql: 'INSERT INTO (foo, bar, baz) VALUES ($1, $2, $3)', | ||
values: [ | ||
1, | ||
2, | ||
3 | ||
] | ||
} | ||
``` | ||
<a name="slonik-value-placeholders-sql-tuplelist"></a> | ||
### <code>sql.tupleList</code> | ||
```js | ||
(tuples: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>) => TupleListSqlTokenType; | ||
``` | ||
Creates a list of tuples (typed row constructs), e.g. | ||
```js | ||
await connection.query(sql` | ||
INSERT INTO (foo, bar, baz) | ||
VALUES ${sql.tupleList([ | ||
[1, 2, 3], | ||
@@ -705,3 +737,3 @@ [4, 5, 6] | ||
{ | ||
sql: 'SELECT ($1, $2, $3), ($4, $5, $6)', | ||
sql: 'INSERT INTO (foo, bar, baz) VALUES ($1, $2, $3), ($4, $5, $6)', | ||
values: [ | ||
@@ -708,0 +740,0 @@ 1, |
@@ -6,8 +6,9 @@ // @flow | ||
IdentifierTokenType, | ||
MultisetSqlTokenType, | ||
PrimitiveValueExpressionType, | ||
RawSqlTokenType, | ||
SetSqlTokenType, | ||
TaggledTemplateLiteralInvocationType, | ||
ValueExpressionType | ||
TupleListSqlTokenType, | ||
TupleSqlTokenType, | ||
ValueExpressionType, | ||
ValueListSqlTokenType | ||
} from '../types'; | ||
@@ -50,3 +51,3 @@ import { | ||
for (const fragmentValue of fragmentValues) { | ||
invariant(isPrimitiveValueExpression(fragmentValue), 'Unexpected set member type.'); | ||
invariant(isPrimitiveValueExpression(fragmentValue), 'Unexpected value binding type.'); | ||
@@ -70,3 +71,3 @@ bindings.push(fragmentValue); | ||
continue; | ||
} else if (value && value.type === 'SET' && Array.isArray(value.members)) { | ||
} else if (value && value.type === 'VALUE_LIST' && Array.isArray(value.values)) { | ||
const placeholders = []; | ||
@@ -76,33 +77,47 @@ | ||
for (const member of value.members) { | ||
for (const listValue of value.values) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
invariant(isPrimitiveValueExpression(member), 'Unexpected set member type.'); | ||
invariant(isPrimitiveValueExpression(listValue), 'Unexpected value list member type.'); | ||
bindings.push(member); | ||
bindings.push(listValue); | ||
} | ||
raw += placeholders.join(', '); | ||
} else if (value && value.type === 'TUPLE' && Array.isArray(value.values)) { | ||
const placeholders = []; | ||
let placeholderIndex = bindings.length; | ||
for (const tupleValue of value.values) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
invariant(isPrimitiveValueExpression(tupleValue), 'Unexpected tuple member type.'); | ||
bindings.push(tupleValue); | ||
} | ||
raw += '(' + placeholders.join(', ') + ')'; | ||
} else if (value && value.type === 'MULTISET' && Array.isArray(value.sets)) { | ||
} else if (value && value.type === 'TUPLE_LIST' && Array.isArray(value.tuples)) { | ||
let placeholderIndex = bindings.length; | ||
const multisetMemberSql = []; | ||
const tupleListMemberSql = []; | ||
let lastSetSize; | ||
let lastTupleSize; | ||
for (const set of value.sets) { | ||
for (const tuple of value.tuples) { | ||
const placeholders = []; | ||
invariant(Array.isArray(set), 'Unexpected set shape.'); | ||
invariant(Array.isArray(tuple), 'Unexpected tuple 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.'); | ||
if (typeof lastTupleSize === 'number' && lastTupleSize !== tuple.length) { | ||
throw new Error('Each tuple in a list of tuples must have an equal number of members.'); | ||
} | ||
lastSetSize = set.length; | ||
lastTupleSize = tuple.length; | ||
for (const member of set) { | ||
for (const member of tuple) { | ||
placeholders.push('$' + ++placeholderIndex); | ||
invariant(isPrimitiveValueExpression(member), 'Unexpected set member type.'); | ||
invariant(isPrimitiveValueExpression(member), 'Unexpected tuple member type.'); | ||
@@ -112,6 +127,6 @@ bindings.push(member); | ||
multisetMemberSql.push('(' + placeholders.join(', ') + ')'); | ||
tupleListMemberSql.push('(' + placeholders.join(', ') + ')'); | ||
} | ||
raw += multisetMemberSql.join(', '); | ||
raw += tupleListMemberSql.join(', '); | ||
} else if (isPrimitiveValueExpression(value)) { | ||
@@ -154,16 +169,23 @@ raw += '$' + (bindings.length + 1); | ||
sql.set = (members: $ReadOnlyArray<PrimitiveValueExpressionType>): SetSqlTokenType => { | ||
sql.valueList = (values: $ReadOnlyArray<PrimitiveValueExpressionType>): ValueListSqlTokenType => { | ||
return { | ||
members, | ||
type: 'SET' | ||
type: 'VALUE_LIST', | ||
values | ||
}; | ||
}; | ||
sql.multiset = (sets: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>): MultisetSqlTokenType => { | ||
sql.tuple = (values: $ReadOnlyArray<PrimitiveValueExpressionType>): TupleSqlTokenType => { | ||
return { | ||
sets, | ||
type: 'MULTISET' | ||
type: 'TUPLE', | ||
values | ||
}; | ||
}; | ||
sql.tupleList = (tuples: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>): TupleListSqlTokenType => { | ||
return { | ||
tuples, | ||
type: 'TUPLE_LIST' | ||
}; | ||
}; | ||
export default sql; |
@@ -144,12 +144,17 @@ // @flow | ||
export type SetSqlTokenType = {| | ||
members: $ReadOnlyArray<PrimitiveValueExpressionType>, | ||
type: 'SET' | ||
export type ValueListSqlTokenType = {| | ||
values: $ReadOnlyArray<PrimitiveValueExpressionType>, | ||
type: 'VALUE_LIST' | ||
|}; | ||
export type MultisetSqlTokenType = {| | ||
sets: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>, | ||
type: 'MULTISET' | ||
export type TupleSqlTokenType = {| | ||
values: $ReadOnlyArray<PrimitiveValueExpressionType>, | ||
type: 'TUPLE' | ||
|}; | ||
export type TupleListSqlTokenType = {| | ||
tuples: $ReadOnlyArray<$ReadOnlyArray<PrimitiveValueExpressionType>>, | ||
type: 'TUPLE_LIST' | ||
|}; | ||
export type PrimitiveValueExpressionType = string | number | boolean | null; | ||
@@ -161,4 +166,5 @@ | ||
RawSqlTokenType | | ||
SetSqlTokenType | | ||
MultisetSqlTokenType; | ||
ValueListSqlTokenType | | ||
TupleSqlTokenType | | ||
TupleListSqlTokenType; | ||
@@ -165,0 +171,0 @@ export type TaggledTemplateLiteralInvocationType = {| |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
244187
2583
1179