mirror of
https://github.com/brianc/node-postgres.git
synced 2025-12-08 20:16:25 +00:00
Move from 3 loops (prepareValue, check for buffers, write param types, write param values) to a single loop. This speeds up the insert benchmark by around 100 queries per second. Performance improvement depends on number of parameters being bound.
188 lines
4.7 KiB
JavaScript
188 lines
4.7 KiB
JavaScript
'use strict'
|
|
|
|
const crypto = require('crypto')
|
|
|
|
const defaults = require('./defaults')
|
|
|
|
function escapeElement(elementRepresentation) {
|
|
var escaped = elementRepresentation.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
|
|
return '"' + escaped + '"'
|
|
}
|
|
|
|
// convert a JS array to a postgres array literal
|
|
// uses comma separator so won't work for types like box that use
|
|
// a different array separator.
|
|
function arrayString(val) {
|
|
var result = '{'
|
|
for (var i = 0; i < val.length; i++) {
|
|
if (i > 0) {
|
|
result = result + ','
|
|
}
|
|
if (val[i] === null || typeof val[i] === 'undefined') {
|
|
result = result + 'NULL'
|
|
} else if (Array.isArray(val[i])) {
|
|
result = result + arrayString(val[i])
|
|
} else if (val[i] instanceof Buffer) {
|
|
result += '\\\\x' + val[i].toString('hex')
|
|
} else {
|
|
result += escapeElement(prepareValue(val[i]))
|
|
}
|
|
}
|
|
result = result + '}'
|
|
return result
|
|
}
|
|
|
|
// converts values from javascript types
|
|
// to their 'raw' counterparts for use as a postgres parameter
|
|
// note: you can override this function to provide your own conversion mechanism
|
|
// for complex types, etc...
|
|
var prepareValue = function (val, seen) {
|
|
// null and undefined are both null for postgres
|
|
if (val == null) {
|
|
return null
|
|
}
|
|
if (val instanceof Buffer) {
|
|
return val
|
|
}
|
|
if (ArrayBuffer.isView(val)) {
|
|
var buf = Buffer.from(val.buffer, val.byteOffset, val.byteLength)
|
|
if (buf.length === val.byteLength) {
|
|
return buf
|
|
}
|
|
return buf.slice(val.byteOffset, val.byteOffset + val.byteLength) // Node.js v4 does not support those Buffer.from params
|
|
}
|
|
if (val instanceof Date) {
|
|
if (defaults.parseInputDatesAsUTC) {
|
|
return dateToStringUTC(val)
|
|
} else {
|
|
return dateToString(val)
|
|
}
|
|
}
|
|
if (Array.isArray(val)) {
|
|
return arrayString(val)
|
|
}
|
|
if (typeof val === 'object') {
|
|
return prepareObject(val, seen)
|
|
}
|
|
return val.toString()
|
|
}
|
|
|
|
function prepareObject(val, seen) {
|
|
if (val && typeof val.toPostgres === 'function') {
|
|
seen = seen || []
|
|
if (seen.indexOf(val) !== -1) {
|
|
throw new Error('circular reference detected while preparing "' + val + '" for query')
|
|
}
|
|
seen.push(val)
|
|
|
|
return prepareValue(val.toPostgres(prepareValue), seen)
|
|
}
|
|
return JSON.stringify(val)
|
|
}
|
|
|
|
function pad(number, digits) {
|
|
number = '' + number
|
|
while (number.length < digits) {
|
|
number = '0' + number
|
|
}
|
|
return number
|
|
}
|
|
|
|
function dateToString(date) {
|
|
var offset = -date.getTimezoneOffset()
|
|
|
|
var year = date.getFullYear()
|
|
var isBCYear = year < 1
|
|
if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
|
|
|
|
var ret =
|
|
pad(year, 4) +
|
|
'-' +
|
|
pad(date.getMonth() + 1, 2) +
|
|
'-' +
|
|
pad(date.getDate(), 2) +
|
|
'T' +
|
|
pad(date.getHours(), 2) +
|
|
':' +
|
|
pad(date.getMinutes(), 2) +
|
|
':' +
|
|
pad(date.getSeconds(), 2) +
|
|
'.' +
|
|
pad(date.getMilliseconds(), 3)
|
|
|
|
if (offset < 0) {
|
|
ret += '-'
|
|
offset *= -1
|
|
} else {
|
|
ret += '+'
|
|
}
|
|
|
|
ret += pad(Math.floor(offset / 60), 2) + ':' + pad(offset % 60, 2)
|
|
if (isBCYear) ret += ' BC'
|
|
return ret
|
|
}
|
|
|
|
function dateToStringUTC(date) {
|
|
var year = date.getUTCFullYear()
|
|
var isBCYear = year < 1
|
|
if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
|
|
|
|
var ret =
|
|
pad(year, 4) +
|
|
'-' +
|
|
pad(date.getUTCMonth() + 1, 2) +
|
|
'-' +
|
|
pad(date.getUTCDate(), 2) +
|
|
'T' +
|
|
pad(date.getUTCHours(), 2) +
|
|
':' +
|
|
pad(date.getUTCMinutes(), 2) +
|
|
':' +
|
|
pad(date.getUTCSeconds(), 2) +
|
|
'.' +
|
|
pad(date.getUTCMilliseconds(), 3)
|
|
|
|
ret += '+00:00'
|
|
if (isBCYear) ret += ' BC'
|
|
return ret
|
|
}
|
|
|
|
function normalizeQueryConfig(config, values, callback) {
|
|
// can take in strings or config objects
|
|
config = typeof config === 'string' ? { text: config } : config
|
|
if (values) {
|
|
if (typeof values === 'function') {
|
|
config.callback = values
|
|
} else {
|
|
config.values = values
|
|
}
|
|
}
|
|
if (callback) {
|
|
config.callback = callback
|
|
}
|
|
return config
|
|
}
|
|
|
|
const md5 = function (string) {
|
|
return crypto.createHash('md5').update(string, 'utf-8').digest('hex')
|
|
}
|
|
|
|
// See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
|
|
const postgresMd5PasswordHash = function (user, password, salt) {
|
|
var inner = md5(password + user)
|
|
var outer = md5(Buffer.concat([Buffer.from(inner), salt]))
|
|
return 'md5' + outer
|
|
}
|
|
|
|
module.exports = {
|
|
prepareValue: function prepareValueWrapper(value) {
|
|
// this ensures that extra arguments do not get passed into prepareValue
|
|
// by accident, eg: from calling values.map(utils.prepareValue)
|
|
return prepareValue(value)
|
|
},
|
|
normalizeQueryConfig,
|
|
postgresMd5PasswordHash,
|
|
md5,
|
|
}
|