mirror of
https://github.com/brianc/node-postgres.git
synced 2025-12-08 20:16:25 +00:00
* Document client.escapeIdentifier and client.escapeLiteral Per #1978 it seems that these client APIs are undocumented. Added documentation for these functions along with some examples and relevant links. * Fix typos in new docs * Migrate escapeIdentifier and escapeLiteral from Client to PG These are standalone utility functions, they do not need a client instance to function. Changes made: - Refactored escapeIdentifer and escapeLiteral from client class to functions in utils - Update PG to export escapeIdentifier and escapeLiteral - Migrated tests for Client.escapeIdentifier and Client.escapeLiteral to tests for utils - Updated documentation, added a "utilities" page where these helpers are discussed **note** this is a breaking change. Users who used these functions (previously undocumented) on instances of Client, or via Client.prototype. * Export escapeIdentifier and escapeLiteral from PG These are standalone utility functions, they should not depend on a client instance. Changes made: - Refactored escapeIdentifer and escapeLiteral from client class to functions in utils - Re-exported functions on client for backwards compatibility - Update PG to export escapeIdentifier and escapeLiteral - Updated tests to validate the newly exported functions from both entry points - Updated documentation, added a "utilities" page where these helpers are discussed * Ensure escape functions work via Client.prototype Updated changes such that escapeIdentifier and escapeLiteral are usable via the client prototype Updated tests to check for both entry points in client
222 lines
5.3 KiB
JavaScript
222 lines
5.3 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
|
|
}
|
|
|
|
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|
const escapeIdentifier = function (str) {
|
|
return '"' + str.replace(/"/g, '""') + '"'
|
|
}
|
|
|
|
const escapeLiteral = function (str) {
|
|
var hasBackslash = false
|
|
var escaped = "'"
|
|
|
|
for (var i = 0; i < str.length; i++) {
|
|
var c = str[i]
|
|
if (c === "'") {
|
|
escaped += c + c
|
|
} else if (c === '\\') {
|
|
escaped += c + c
|
|
hasBackslash = true
|
|
} else {
|
|
escaped += c
|
|
}
|
|
}
|
|
|
|
escaped += "'"
|
|
|
|
if (hasBackslash === true) {
|
|
escaped = ' E' + escaped
|
|
}
|
|
|
|
return escaped
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
escapeIdentifier,
|
|
escapeLiteral
|
|
}
|