Skip to content

Commit 5fe4047

Browse files
authored
Add support for named parameters (#16)
* Include library node-postgres-named https://github.com/bwestergard/node-postgres-named * eslint fix + format * Simplify credits * Remove lodash dependency * Add tests https://github.com/bwestergard/node-postgres-named/blob/master/test/test.js * Simplification, removed libraries, and integration
1 parent 76df2fb commit 5fe4047

File tree

7 files changed

+1256
-108
lines changed

7 files changed

+1256
-108
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ SELECT * FROM table WHERE id = '{{{ msg.id }}}';
3737

3838
As an alternative to using the query template above, this node also accepts an SQL query via the `msg.query` parameter.
3939

40-
### Parameterized query
40+
### Parameterized query (numeric)
4141

4242
Parameters for parameterized queries can be passed as a parameter array `msg.params`:
4343

@@ -51,6 +51,24 @@ msg.params = [ msg.id ];
5151
SELECT * FROM table WHERE id = $1;
5252
```
5353

54+
### Named parameterized query
55+
56+
As an alternative to numeric parameters,
57+
named parameters for parameterized queries can be passed as a parameter array `msg.queryParameters`:
58+
59+
```js
60+
// In a function, provide parameters for the named parameterized query
61+
msg.queryParameters.id = msg.id;
62+
```
63+
64+
```sql
65+
-- In this node, use a named parameterized query
66+
SELECT * FROM table WHERE id = $id;
67+
```
68+
69+
_Note_: named parameters are not natively supported by PostgreSQL, and this library just emulates them,
70+
so this is less robust than numeric parameters.
71+
5472
### Dynamic PostgreSQL connection parameters
5573

5674
If the information about which database server to connect and how needs to be dynamic,

locales/en-US/postgresql.html

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,32 @@ <h4>Parameterized queries</h4>
4242
SELECT * FROM table WHERE id = $1
4343
</pre>
4444

45+
<h4>Parameterized queries</h4>
46+
<p>
47+
As an alternative to numeric parameters,
48+
named parameters for parameterized queries can be passed as a parameter arrayy <code>msg.queryParameters</code>:
49+
</p>
50+
<pre>
51+
// In a function, provide parameters for the named parameterized query
52+
msg.queryParameters.id = msg.id;
53+
</pre>
54+
55+
<pre>
56+
-- In this node, use a named parameterized query
57+
SELECT * FROM table WHERE id = $id;
58+
</pre>
59+
60+
<p>
61+
<em>Note</em>: named parameters are not natively supported by PostgreSQL, and this library just emulates them,
62+
so this is less robust than numeric parameters.
63+
</p>
64+
4565
<h4>Dynamic PostgreSQL connection parameters</h4>
4666
<p>
4767
If the information about which database server to connect and how needs to be dynamic,
4868
it is possible to pass a <a href="https://node-postgres.com/api/client">custom client configuration</a> in the message:
4969
</p>
50-
70+
5171
<pre>
5272
msg.pgConfig = {
5373
user?: string, // default process.env.PGUSER || process.env.USER
@@ -85,7 +105,7 @@ <h3>Backpressure</h3>
85105
and a truthy <code>node.tickProvider</code> for upstream nodes.
86106
Likewise, this node detects upstream nodes using the same back-pressure convention, and automatically sends ticks.
87107
</p>
88-
108+
89109
<h3>Sequences for split results</h3>
90110
<p>
91111
When the <em>Split results</em> option is enabled (streaming), the messages contain some information following the conventions

node-postgres-named.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
3+
/**
4+
* Rewritten subset of the library https://github.com/bwestergard/node-postgres-named/blob/master/main.js
5+
* https://github.com/ksteckert/node-postgres-named/tree/patch-1
6+
*/
7+
8+
const tokenPattern = /(?<=\$)[a-zA-Z]([a-zA-Z0-9_]*)\b/g;
9+
10+
function numericFromNamed(sql, parameters) {
11+
const fillableTokens = new Set(Object.keys(parameters));
12+
const matchedTokens = new Set(sql.match(tokenPattern));
13+
14+
const unmatchedTokens = Array.from(matchedTokens).filter((token) => !fillableTokens.has(token));
15+
if (unmatchedTokens.length > 0) {
16+
throw new Error('Missing Parameters: ' + unmatchedTokens.join(', '));
17+
}
18+
19+
const fillTokens = Array.from(matchedTokens).filter((token) => fillableTokens.has(token)).sort();
20+
const fillValues = fillTokens.map((token) => parameters[token]);
21+
22+
const interpolatedSql = fillTokens.reduce((partiallyInterpolated, token, index) => {
23+
const replaceAllPattern = new RegExp('\\$' + fillTokens[index] + '\\b', 'g');
24+
return partiallyInterpolated.replace(replaceAllPattern, '$' + (index + 1)); // PostgreSQL parameters are 1-indexed
25+
}, sql);
26+
27+
return {
28+
text: interpolatedSql,
29+
values: fillValues,
30+
};
31+
}
32+
33+
module.exports.convert = numericFromNamed;

0 commit comments

Comments
 (0)