Skip to content

Commit 86445ca

Browse files
committed
Support nested fragments values and dynamics - fixes porsager#326
1 parent 50403a1 commit 86445ca

File tree

3 files changed

+69
-40
lines changed

3 files changed

+69
-40
lines changed

src/connection.js

+3-22
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import tls from 'tls'
33
import crypto from 'crypto'
44
import Stream from 'stream'
55

6-
import { Identifier, Builder, handleValue, arrayParser, arraySerializer } from './types.js'
6+
import { stringify, handleValue, arrayParser, arraySerializer } from './types.js'
77
import { Errors } from './errors.js'
88
import Result from './result.js'
99
import Queue from './queue.js'
@@ -218,9 +218,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
218218
const parameters = []
219219
, types = []
220220

221-
const string = stringify(q, q.strings[0], q.args[0], parameters, types)
221+
const string = stringify(q, q.strings[0], q.args[0], parameters, types, options)
222222

223-
!q.tagged && q.args.forEach(x => handleValue(x, parameters, types))
223+
!q.tagged && q.args.forEach(x => handleValue(x, parameters, types, options))
224224

225225
q.prepare = options.prepare && ('prepare' in q.options ? q.options.prepare : true)
226226
q.string = string
@@ -236,25 +236,6 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
236236
typeof options.debug === 'function' && options.debug(id, string, parameters, types)
237237
}
238238

239-
function stringify(q, string, value, parameters, types) {
240-
for (let i = 1; i < q.strings.length; i++) {
241-
string += (
242-
value instanceof Query ? fragment(string, value, parameters, types) :
243-
value instanceof Identifier ? value.value :
244-
value instanceof Builder ? value.build(string, parameters, types, options.transform) :
245-
handleValue(value, parameters, types)
246-
) + q.strings[i]
247-
value = q.args[i]
248-
}
249-
250-
return string
251-
}
252-
253-
function fragment(string, q, parameters, types) {
254-
q.fragment = true
255-
return stringify(q, q.strings[0], q.args[0], parameters, types)
256-
}
257-
258239
function write(x, fn) {
259240
chunk = chunk ? Buffer.concat([chunk, x]) : Buffer.from(x)
260241
if (fn || chunk.length >= 1024)

src/types.js

+37-18
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ export class Builder extends NotTagged {
6464
this.rest = rest
6565
}
6666

67-
build(before, parameters, types, transform) {
67+
build(before, parameters, types, options) {
6868
const keyword = builders.map(([x, fn]) => ({ fn, i: before.search(x) })).sort((a, b) => a.i - b.i).pop()
6969
if (keyword.i === -1)
7070
throw new Error('Could not infer helper mode')
7171

72-
return keyword.fn(this.first, this.rest, parameters, types, transform)
72+
return keyword.fn(this.first, this.rest, parameters, types, options)
7373
}
7474
}
7575

@@ -90,40 +90,59 @@ export function handleValue(x, parameters, types) {
9090

9191
const defaultHandlers = typeHandlers(types)
9292

93-
function valuesBuilder(first, parameters, types, transform, columns) {
93+
export function stringify(q, string, value, parameters, types, options) { // eslint-disable-line
94+
for (let i = 1; i < q.strings.length; i++) {
95+
string += (
96+
value instanceof Query ? fragment(value, parameters, types) :
97+
value instanceof Identifier ? value.value :
98+
value instanceof Builder ? value.build(string, parameters, types, options) :
99+
handleValue(value, parameters, types, options)
100+
) + q.strings[i]
101+
value = q.args[i]
102+
}
103+
104+
return string
105+
}
106+
107+
function fragment(q, parameters, types) {
108+
q.fragment = true
109+
return stringify(q, q.strings[0], q.args[0], parameters, types)
110+
}
111+
112+
function valuesBuilder(first, parameters, types, columns, options) {
94113
let value
95114
return first.map(row =>
96115
'(' + columns.map(column => {
97116
value = row[column]
98117
return (
99-
value instanceof Query ? value.strings[0] :
118+
value instanceof Query ? fragment(value, parameters, types) :
100119
value instanceof Identifier ? value.value :
101-
handleValue(value, parameters, types)
120+
handleValue(value, parameters, types, options)
102121
)
103122
}).join(',') + ')'
104123
).join(',')
105124
}
106125

107-
function values(first, rest, parameters, types, transform) {
126+
function values(first, rest, parameters, types, options) {
108127
const multi = Array.isArray(first[0])
109128
const columns = rest.length ? rest.flat() : Object.keys(multi ? first[0] : first)
110-
return valuesBuilder(multi ? first : [first], parameters, types, transform, columns)
129+
return valuesBuilder(multi ? first : [first], parameters, types, columns, options)
111130
}
112131

113-
function select(first, rest, parameters, types, transform) {
132+
function select(first, rest, parameters, types, options) {
114133
typeof first === 'string' && (first = [first].concat(rest))
115134
if (Array.isArray(first))
116-
return first.map(x => escapeIdentifier(transform.column.to ? transform.column.to(x) : x)).join(',')
135+
return first.map(x => escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)).join(',')
117136

118137
let value
119138
const columns = rest.length ? rest.flat() : Object.keys(first)
120139
return columns.map(x => {
121140
value = first[x]
122141
return (
123-
value instanceof Query ? value.strings[0] :
142+
value instanceof Query ? fragment(value, parameters, types) :
124143
value instanceof Identifier ? value.value :
125-
handleValue(value, parameters, types)
126-
) + ' as ' + escapeIdentifier(transform.column.to ? transform.column.to(x) : x)
144+
handleValue(value, parameters, types, options)
145+
) + ' as ' + escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)
127146
}).join(',')
128147
}
129148

@@ -133,19 +152,19 @@ const builders = Object.entries({
133152
select,
134153
returning: select,
135154

136-
update(first, rest, parameters, types, transform) {
155+
update(first, rest, parameters, types, options) {
137156
return (rest.length ? rest.flat() : Object.keys(first)).map(x =>
138-
escapeIdentifier(transform.column.to ? transform.column.to(x) : x) +
139-
'=' + handleValue(first[x], parameters, types)
157+
escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x) +
158+
'=' + handleValue(first[x], parameters, types, options)
140159
)
141160
},
142161

143-
insert(first, rest, parameters, types, transform) {
162+
insert(first, rest, parameters, types, options) {
144163
const columns = rest.length ? rest.flat() : Object.keys(Array.isArray(first) ? first[0] : first)
145164
return '(' + columns.map(x =>
146-
escapeIdentifier(transform.column.to ? transform.column.to(x) : x)
165+
escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)
147166
).join(',') + ')values' +
148-
valuesBuilder(Array.isArray(first) ? first : [first], parameters, types, transform, columns)
167+
valuesBuilder(Array.isArray(first) ? first : [first], parameters, types, columns, options)
149168
}
150169
}).map(([x, fn]) => ([new RegExp('(^|[\\s(])' + x + '($|[\\s(])', 'i'), fn]))
151170

tests/index.js

+29
Original file line numberDiff line numberDiff line change
@@ -2007,3 +2007,32 @@ t('Ensure drain only dequeues if ready', async() => {
20072007

20082008
return [res.length, 2]
20092009
})
2010+
2011+
t('Supports fragments as dynamic parameters', async() => {
2012+
await sql`create table test (a int, b bool)`
2013+
await sql`insert into test values(1, true)`
2014+
await sql`insert into test ${
2015+
sql({
2016+
a: 2,
2017+
b: sql`exists(select 1 from test where b = ${ true })`
2018+
})
2019+
}`
2020+
2021+
return [
2022+
'1,t2,t',
2023+
(await sql`select * from test`.raw()).join(''),
2024+
await sql`drop table test`
2025+
]
2026+
})
2027+
2028+
t('Supports nested fragments with parameters', async() => {
2029+
await sql`create table test ${
2030+
sql`(${ sql('a') } ${ sql`int` })`
2031+
}`
2032+
await sql`insert into test values(1)`
2033+
return [
2034+
1,
2035+
(await sql`select a from test`)[0].a,
2036+
await sql`drop table test`
2037+
]
2038+
})

0 commit comments

Comments
 (0)