Skip to content

Commit a70eb81

Browse files
committed
Remove js type inference
1 parent 58aac20 commit a70eb81

File tree

9 files changed

+92
-90
lines changed

9 files changed

+92
-90
lines changed

cjs/src/connection.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const tls = require('tls')
33
const crypto = require('crypto')
44
const Stream = require('stream')
55

6-
const { stringify, handleValue, arrayParser, arraySerializer } = require('./types.js')
6+
const { serialize, stringify, handleValue, arrayParser, arraySerializer } = require('./types.js')
77
const { Errors } = require('./errors.js')
88
const Result = require('./result.js')
99
const Queue = require('./queue.js')
@@ -180,7 +180,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
180180
throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
181181

182182
return q.options.simple
183-
? b().Q().str(q.strings[0] + b.N).end()
183+
? b().Q().str(q.statement.string + b.N).end()
184184
: q.describeFirst
185185
? Buffer.concat([describe(q), Flush])
186186
: q.prepare
@@ -912,7 +912,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
912912
type = types[i]
913913
parameters[i] = x = type in options.serializers
914914
? options.serializers[type](x)
915-
: '' + x
915+
: serialize(x)
916916

917917
prev = b.i
918918
b.inc(4).str(x).i32(b.i - prev - 4, prev)

cjs/src/types.js

+21-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
const { Query } = require('./query.js')
22
const { Errors } = require('./errors.js')
33

4+
const serialize = module.exports.serialize = function serialize(x) {
5+
return typeof x === 'string' ? x :
6+
x instanceof Date ? types.date.serialize(x) :
7+
x instanceof Uint8Array ? types.bytea.serialize(x) :
8+
(x === true || x === false) ? types.boolean.serialize(x) :
9+
'' + x
10+
}
11+
412
const types = module.exports.types = {
513
string: {
614
to: 25,
715
from: null, // defaults to string
8-
serialize: x => '' + x
16+
serialize
917
},
1018
number: {
1119
to: 0,
@@ -66,10 +74,9 @@ const Builder = module.exports.Builder = class Builder extends NotTagged {
6674

6775
build(before, parameters, types, options) {
6876
const keyword = builders.map(([x, fn]) => ({ fn, i: before.search(x) })).sort((a, b) => a.i - b.i).pop()
69-
if (keyword.i === -1)
70-
throw new Error('Could not infer helper mode')
71-
72-
return keyword.fn(this.first, this.rest, parameters, types, options)
77+
return keyword.i === -1
78+
? escapeIdentifiers(this.first, options)
79+
: keyword.fn(this.first, this.rest, parameters, types, options)
7380
}
7481
}
7582

@@ -137,7 +144,7 @@ function values(first, rest, parameters, types, options) {
137144
function select(first, rest, parameters, types, options) {
138145
typeof first === 'string' && (first = [first].concat(rest))
139146
if (Array.isArray(first))
140-
return first.map(x => escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)).join(',')
147+
return escapeIdentifiers(first, options)
141148

142149
let value
143150
const columns = rest.length ? rest.flat() : Object.keys(first)
@@ -170,9 +177,7 @@ const builders = Object.entries({
170177

171178
insert(first, rest, parameters, types, options) {
172179
const columns = rest.length ? rest.flat() : Object.keys(Array.isArray(first) ? first[0] : first)
173-
return '(' + columns.map(x =>
174-
escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)
175-
).join(',') + ')values' +
180+
return '(' + escapeIdentifiers(columns, options) + ')values' +
176181
valuesBuilder(Array.isArray(first) ? first : [first], parameters, types, columns, options)
177182
}
178183
}).map(([x, fn]) => ([new RegExp('((?:^|[\\s(])' + x + '(?:$|[\\s(]))(?![\\s\\S]*\\1)', 'i'), fn]))
@@ -209,20 +214,18 @@ function typeHandlers(types) {
209214
}, { parsers: {}, serializers: {} })
210215
}
211216

217+
function escapeIdentifiers(xs, { transform: { column } }) {
218+
return xs.map(x => escapeIdentifier(column.to ? column.to(x) : x)).join(',')
219+
}
220+
212221
const escapeIdentifier = module.exports.escapeIdentifier = function escape(str) {
213222
return '"' + str.replace(/"/g, '""').replace(/\./g, '"."') + '"'
214223
}
215224

216225
const inferType = module.exports.inferType = function inferType(x) {
217-
return (
218-
x instanceof Parameter ? x.type :
219-
x instanceof Date ? 1184 :
220-
x instanceof Uint8Array ? 17 :
221-
(x === true || x === false) ? 16 :
222-
typeof x === 'bigint' ? 20 :
223-
Array.isArray(x) ? inferType(x[0]) :
224-
0
225-
)
226+
return x instanceof Parameter
227+
? x.type
228+
: 0
226229
}
227230

228231
const escapeBackslash = /\\/g

cjs/tests/index.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,16 @@ t('String', async() =>
8989
)
9090

9191
t('Boolean false', async() =>
92-
[false, (await sql`select ${ false } as x`)[0].x]
92+
[false, (await sql`select ${ false }::bool as x`)[0].x]
9393
)
9494

9595
t('Boolean true', async() =>
96-
[true, (await sql`select ${ true } as x`)[0].x]
96+
[true, (await sql`select ${ true }::bool as x`)[0].x]
9797
)
9898

9999
t('Date', async() => {
100100
const now = new Date()
101-
return [0, now - (await sql`select ${ now } as x`)[0].x]
101+
return [0, now - (await sql`select ${ now }::timestamptz as x`)[0].x]
102102
})
103103

104104
t('Json', async() => {
@@ -134,7 +134,8 @@ t('Array of String', async() =>
134134

135135
t('Array of Date', async() => {
136136
const now = new Date()
137-
return [now.getTime(), (await sql`select ${ sql.array([now, now, now]) } as x`)[0].x[2].getTime()]
137+
, iso = now.toISOString()
138+
return [now.getTime(), (await sql`select ${ sql.array([iso, iso, iso]) }::timestamptz[] as x`)[0].x[2].getTime()]
138139
})
139140

140141
t('Nested array n2', async() =>
@@ -482,7 +483,7 @@ t('Point type array', async() => {
482483
})
483484

484485
await sql`create table test (x point[])`
485-
await sql`insert into test (x) values (${ sql.array([sql.types.point([10, 20]), sql.types.point([20, 30])]) })`
486+
await sql`insert into test (x) values (${ [sql.types.point([10, 20]), sql.types.point([20, 30])] })`
486487
return [30, (await sql`select x from test`)[0].x[1][1], await sql`drop table test`]
487488
})
488489

@@ -1194,10 +1195,7 @@ t('Multiple queries', async() => {
11941195
})
11951196

11961197
t('Multiple statements', async() =>
1197-
[2, await sql.unsafe(`
1198-
select 1 as x;
1199-
select 2 as a;
1200-
`).then(([, [x]]) => x.a)]
1198+
[2, await sql.unsafe(`select 1 as x;select 2 as a`).then(([, [x]]) => x.a)]
12011199
)
12021200

12031201
t('throws correct error when authentication fails', async() => {
@@ -1348,7 +1346,7 @@ t('Multiple Cursors', { timeout: 2 }, async() => {
13481346

13491347
t('Cursor as async iterator', async() => {
13501348
const order = []
1351-
for await (const [x] of sql`select generate_series(1,2) as x;`.cursor()) {
1349+
for await (const [x] of sql`select generate_series(1,2) as x`.cursor()) {
13521350
order.push(x.x + 'a')
13531351
await delay(10)
13541352
order.push(x.x + 'b')
@@ -1359,7 +1357,7 @@ t('Cursor as async iterator', async() => {
13591357

13601358
t('Cursor as async iterator with break', async() => {
13611359
const order = []
1362-
for await (const xs of sql`select generate_series(1,2) as x;`.cursor()) {
1360+
for await (const xs of sql`select generate_series(1,2) as x`.cursor()) {
13631361
order.push(xs[0].x + 'a')
13641362
await delay(10)
13651363
order.push(xs[0].x + 'b')
@@ -1748,7 +1746,7 @@ t('Recreate prepared statements on transformAssignedExpr error', { timeout: 1 },
17481746

17491747
t('Throws correct error when retrying in transactions', async() => {
17501748
await sql`create table test(x int)`
1751-
const error = await sql.begin(sql => sql`insert into test (x) values (${ false })`).catch(e => e)
1749+
const error = await sql.begin(sql => sql`insert into test (x) values (${ false }::bool)`).catch(e => e)
17521750
return [
17531751
error.code,
17541752
'42804',

deno/src/connection.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { tls } from '../polyfills.js'
77
import crypto from 'https://deno.land/[email protected]/node/crypto.ts'
88
import Stream from 'https://deno.land/[email protected]/node/stream.ts'
99

10-
import { stringify, handleValue, arrayParser, arraySerializer } from './types.js'
10+
import { serialize, stringify, handleValue, arrayParser, arraySerializer } from './types.js'
1111
import { Errors } from './errors.js'
1212
import Result from './result.js'
1313
import Queue from './queue.js'
@@ -184,7 +184,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
184184
throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
185185

186186
return q.options.simple
187-
? b().Q().str(q.strings[0] + b.N).end()
187+
? b().Q().str(q.statement.string + b.N).end()
188188
: q.describeFirst
189189
? Buffer.concat([describe(q), Flush])
190190
: q.prepare
@@ -916,7 +916,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
916916
type = types[i]
917917
parameters[i] = x = type in options.serializers
918918
? options.serializers[type](x)
919-
: '' + x
919+
: serialize(x)
920920

921921
prev = b.i
922922
b.inc(4).str(x).i32(b.i - prev - 4, prev)

deno/src/types.js

+21-18
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ import { Buffer } from 'https://deno.land/[email protected]/node/buffer.ts'
22
import { Query } from './query.js'
33
import { Errors } from './errors.js'
44

5+
export const serialize = function serialize(x) {
6+
return typeof x === 'string' ? x :
7+
x instanceof Date ? types.date.serialize(x) :
8+
x instanceof Uint8Array ? types.bytea.serialize(x) :
9+
(x === true || x === false) ? types.boolean.serialize(x) :
10+
'' + x
11+
}
12+
513
export const types = {
614
string: {
715
to: 25,
816
from: null, // defaults to string
9-
serialize: x => '' + x
17+
serialize
1018
},
1119
number: {
1220
to: 0,
@@ -67,10 +75,9 @@ export class Builder extends NotTagged {
6775

6876
build(before, parameters, types, options) {
6977
const keyword = builders.map(([x, fn]) => ({ fn, i: before.search(x) })).sort((a, b) => a.i - b.i).pop()
70-
if (keyword.i === -1)
71-
throw new Error('Could not infer helper mode')
72-
73-
return keyword.fn(this.first, this.rest, parameters, types, options)
78+
return keyword.i === -1
79+
? escapeIdentifiers(this.first, options)
80+
: keyword.fn(this.first, this.rest, parameters, types, options)
7481
}
7582
}
7683

@@ -138,7 +145,7 @@ function values(first, rest, parameters, types, options) {
138145
function select(first, rest, parameters, types, options) {
139146
typeof first === 'string' && (first = [first].concat(rest))
140147
if (Array.isArray(first))
141-
return first.map(x => escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)).join(',')
148+
return escapeIdentifiers(first, options)
142149

143150
let value
144151
const columns = rest.length ? rest.flat() : Object.keys(first)
@@ -171,9 +178,7 @@ const builders = Object.entries({
171178

172179
insert(first, rest, parameters, types, options) {
173180
const columns = rest.length ? rest.flat() : Object.keys(Array.isArray(first) ? first[0] : first)
174-
return '(' + columns.map(x =>
175-
escapeIdentifier(options.transform.column.to ? options.transform.column.to(x) : x)
176-
).join(',') + ')values' +
181+
return '(' + escapeIdentifiers(columns, options) + ')values' +
177182
valuesBuilder(Array.isArray(first) ? first : [first], parameters, types, columns, options)
178183
}
179184
}).map(([x, fn]) => ([new RegExp('((?:^|[\\s(])' + x + '(?:$|[\\s(]))(?![\\s\\S]*\\1)', 'i'), fn]))
@@ -210,20 +215,18 @@ function typeHandlers(types) {
210215
}, { parsers: {}, serializers: {} })
211216
}
212217

218+
function escapeIdentifiers(xs, { transform: { column } }) {
219+
return xs.map(x => escapeIdentifier(column.to ? column.to(x) : x)).join(',')
220+
}
221+
213222
export const escapeIdentifier = function escape(str) {
214223
return '"' + str.replace(/"/g, '""').replace(/\./g, '"."') + '"'
215224
}
216225

217226
export const inferType = function inferType(x) {
218-
return (
219-
x instanceof Parameter ? x.type :
220-
x instanceof Date ? 1184 :
221-
x instanceof Uint8Array ? 17 :
222-
(x === true || x === false) ? 16 :
223-
typeof x === 'bigint' ? 20 :
224-
Array.isArray(x) ? inferType(x[0]) :
225-
0
226-
)
227+
return x instanceof Parameter
228+
? x.type
229+
: 0
227230
}
228231

229232
const escapeBackslash = /\\/g

deno/tests/index.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,16 @@ t('String', async() =>
9191
)
9292

9393
t('Boolean false', async() =>
94-
[false, (await sql`select ${ false } as x`)[0].x]
94+
[false, (await sql`select ${ false }::bool as x`)[0].x]
9595
)
9696

9797
t('Boolean true', async() =>
98-
[true, (await sql`select ${ true } as x`)[0].x]
98+
[true, (await sql`select ${ true }::bool as x`)[0].x]
9999
)
100100

101101
t('Date', async() => {
102102
const now = new Date()
103-
return [0, now - (await sql`select ${ now } as x`)[0].x]
103+
return [0, now - (await sql`select ${ now }::timestamptz as x`)[0].x]
104104
})
105105

106106
t('Json', async() => {
@@ -136,7 +136,8 @@ t('Array of String', async() =>
136136

137137
t('Array of Date', async() => {
138138
const now = new Date()
139-
return [now.getTime(), (await sql`select ${ sql.array([now, now, now]) } as x`)[0].x[2].getTime()]
139+
, iso = now.toISOString()
140+
return [now.getTime(), (await sql`select ${ sql.array([iso, iso, iso]) }::timestamptz[] as x`)[0].x[2].getTime()]
140141
})
141142

142143
t('Nested array n2', async() =>
@@ -484,7 +485,7 @@ t('Point type array', async() => {
484485
})
485486

486487
await sql`create table test (x point[])`
487-
await sql`insert into test (x) values (${ sql.array([sql.types.point([10, 20]), sql.types.point([20, 30])]) })`
488+
await sql`insert into test (x) values (${ [sql.types.point([10, 20]), sql.types.point([20, 30])] })`
488489
return [30, (await sql`select x from test`)[0].x[1][1], await sql`drop table test`]
489490
})
490491

@@ -1196,10 +1197,7 @@ t('Multiple queries', async() => {
11961197
})
11971198

11981199
t('Multiple statements', async() =>
1199-
[2, await sql.unsafe(`
1200-
select 1 as x;
1201-
select 2 as a;
1202-
`).then(([, [x]]) => x.a)]
1200+
[2, await sql.unsafe(`select 1 as x;select 2 as a`).then(([, [x]]) => x.a)]
12031201
)
12041202

12051203
t('throws correct error when authentication fails', async() => {
@@ -1350,7 +1348,7 @@ t('Multiple Cursors', { timeout: 2 }, async() => {
13501348

13511349
t('Cursor as async iterator', async() => {
13521350
const order = []
1353-
for await (const [x] of sql`select generate_series(1,2) as x;`.cursor()) {
1351+
for await (const [x] of sql`select generate_series(1,2) as x`.cursor()) {
13541352
order.push(x.x + 'a')
13551353
await delay(10)
13561354
order.push(x.x + 'b')
@@ -1361,7 +1359,7 @@ t('Cursor as async iterator', async() => {
13611359

13621360
t('Cursor as async iterator with break', async() => {
13631361
const order = []
1364-
for await (const xs of sql`select generate_series(1,2) as x;`.cursor()) {
1362+
for await (const xs of sql`select generate_series(1,2) as x`.cursor()) {
13651363
order.push(xs[0].x + 'a')
13661364
await delay(10)
13671365
order.push(xs[0].x + 'b')
@@ -1750,7 +1748,7 @@ t('Recreate prepared statements on transformAssignedExpr error', { timeout: 1 },
17501748

17511749
t('Throws correct error when retrying in transactions', async() => {
17521750
await sql`create table test(x int)`
1753-
const error = await sql.begin(sql => sql`insert into test (x) values (${ false })`).catch(e => e)
1751+
const error = await sql.begin(sql => sql`insert into test (x) values (${ false }::bool)`).catch(e => e)
17541752
return [
17551753
error.code,
17561754
'42804',

src/connection.js

+2-2
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 { stringify, handleValue, arrayParser, arraySerializer } from './types.js'
6+
import { serialize, 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'
@@ -912,7 +912,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
912912
type = types[i]
913913
parameters[i] = x = type in options.serializers
914914
? options.serializers[type](x)
915-
: '' + x
915+
: serialize(x)
916916

917917
prev = b.i
918918
b.inc(4).str(x).i32(b.i - prev - 4, prev)

0 commit comments

Comments
 (0)