| [62b2964] | 1 | 'use strict'
|
|---|
| 2 |
|
|---|
| 3 | const defaults = require('./defaults')
|
|---|
| 4 |
|
|---|
| 5 | const { isDate } = require('util/types')
|
|---|
| 6 |
|
|---|
| 7 | function escapeElement(elementRepresentation) {
|
|---|
| 8 | const escaped = elementRepresentation.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|---|
| 9 |
|
|---|
| 10 | return '"' + escaped + '"'
|
|---|
| 11 | }
|
|---|
| 12 |
|
|---|
| 13 | // convert a JS array to a postgres array literal
|
|---|
| 14 | // uses comma separator so won't work for types like box that use
|
|---|
| 15 | // a different array separator.
|
|---|
| 16 | function arrayString(val) {
|
|---|
| 17 | let result = '{'
|
|---|
| 18 | for (let i = 0; i < val.length; i++) {
|
|---|
| 19 | if (i > 0) {
|
|---|
| 20 | result += ','
|
|---|
| 21 | }
|
|---|
| 22 | let item = val[i]
|
|---|
| 23 | if (item == null) {
|
|---|
| 24 | result += 'NULL'
|
|---|
| 25 | } else if (Array.isArray(item)) {
|
|---|
| 26 | result += arrayString(item)
|
|---|
| 27 | } else if (ArrayBuffer.isView(item)) {
|
|---|
| 28 | if (!(item instanceof Buffer)) {
|
|---|
| 29 | item = Buffer.from(item.buffer, item.byteOffset, item.byteLength)
|
|---|
| 30 | }
|
|---|
| 31 | result += '\\\\x' + item.toString('hex')
|
|---|
| 32 | } else {
|
|---|
| 33 | result += escapeElement(prepareValue(item))
|
|---|
| 34 | }
|
|---|
| 35 | }
|
|---|
| 36 | result += '}'
|
|---|
| 37 | return result
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | // converts values from javascript types
|
|---|
| 41 | // to their 'raw' counterparts for use as a postgres parameter
|
|---|
| 42 | // note: you can override this function to provide your own conversion mechanism
|
|---|
| 43 | // for complex types, etc...
|
|---|
| 44 | const prepareValue = function (val, seen) {
|
|---|
| 45 | // null and undefined are both null for postgres
|
|---|
| 46 | if (val == null) {
|
|---|
| 47 | return null
|
|---|
| 48 | }
|
|---|
| 49 | if (typeof val === 'object') {
|
|---|
| 50 | if (val instanceof Buffer) {
|
|---|
| 51 | return val
|
|---|
| 52 | }
|
|---|
| 53 | if (ArrayBuffer.isView(val)) {
|
|---|
| 54 | return Buffer.from(val.buffer, val.byteOffset, val.byteLength)
|
|---|
| 55 | }
|
|---|
| 56 | if (isDate(val)) {
|
|---|
| 57 | if (defaults.parseInputDatesAsUTC) {
|
|---|
| 58 | return dateToStringUTC(val)
|
|---|
| 59 | } else {
|
|---|
| 60 | return dateToString(val)
|
|---|
| 61 | }
|
|---|
| 62 | }
|
|---|
| 63 | if (Array.isArray(val)) {
|
|---|
| 64 | return arrayString(val)
|
|---|
| 65 | }
|
|---|
| 66 |
|
|---|
| 67 | return prepareObject(val, seen)
|
|---|
| 68 | }
|
|---|
| 69 | return val.toString()
|
|---|
| 70 | }
|
|---|
| 71 |
|
|---|
| 72 | function prepareObject(val, seen) {
|
|---|
| 73 | if (val && typeof val.toPostgres === 'function') {
|
|---|
| 74 | seen = seen || []
|
|---|
| 75 | if (seen.indexOf(val) !== -1) {
|
|---|
| 76 | throw new Error('circular reference detected while preparing "' + val + '" for query')
|
|---|
| 77 | }
|
|---|
| 78 | seen.push(val)
|
|---|
| 79 |
|
|---|
| 80 | return prepareValue(val.toPostgres(prepareValue), seen)
|
|---|
| 81 | }
|
|---|
| 82 | return JSON.stringify(val)
|
|---|
| 83 | }
|
|---|
| 84 |
|
|---|
| 85 | function dateToString(date) {
|
|---|
| 86 | let offset = -date.getTimezoneOffset()
|
|---|
| 87 |
|
|---|
| 88 | let year = date.getFullYear()
|
|---|
| 89 | const isBCYear = year < 1
|
|---|
| 90 | if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
|
|---|
| 91 |
|
|---|
| 92 | let ret =
|
|---|
| 93 | String(year).padStart(4, '0') +
|
|---|
| 94 | '-' +
|
|---|
| 95 | String(date.getMonth() + 1).padStart(2, '0') +
|
|---|
| 96 | '-' +
|
|---|
| 97 | String(date.getDate()).padStart(2, '0') +
|
|---|
| 98 | 'T' +
|
|---|
| 99 | String(date.getHours()).padStart(2, '0') +
|
|---|
| 100 | ':' +
|
|---|
| 101 | String(date.getMinutes()).padStart(2, '0') +
|
|---|
| 102 | ':' +
|
|---|
| 103 | String(date.getSeconds()).padStart(2, '0') +
|
|---|
| 104 | '.' +
|
|---|
| 105 | String(date.getMilliseconds()).padStart(3, '0')
|
|---|
| 106 |
|
|---|
| 107 | if (offset < 0) {
|
|---|
| 108 | ret += '-'
|
|---|
| 109 | offset *= -1
|
|---|
| 110 | } else {
|
|---|
| 111 | ret += '+'
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | ret += String(Math.floor(offset / 60)).padStart(2, '0') + ':' + String(offset % 60).padStart(2, '0')
|
|---|
| 115 | if (isBCYear) ret += ' BC'
|
|---|
| 116 | return ret
|
|---|
| 117 | }
|
|---|
| 118 |
|
|---|
| 119 | function dateToStringUTC(date) {
|
|---|
| 120 | let year = date.getUTCFullYear()
|
|---|
| 121 | const isBCYear = year < 1
|
|---|
| 122 | if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
|
|---|
| 123 |
|
|---|
| 124 | let ret =
|
|---|
| 125 | String(year).padStart(4, '0') +
|
|---|
| 126 | '-' +
|
|---|
| 127 | String(date.getUTCMonth() + 1).padStart(2, '0') +
|
|---|
| 128 | '-' +
|
|---|
| 129 | String(date.getUTCDate()).padStart(2, '0') +
|
|---|
| 130 | 'T' +
|
|---|
| 131 | String(date.getUTCHours()).padStart(2, '0') +
|
|---|
| 132 | ':' +
|
|---|
| 133 | String(date.getUTCMinutes()).padStart(2, '0') +
|
|---|
| 134 | ':' +
|
|---|
| 135 | String(date.getUTCSeconds()).padStart(2, '0') +
|
|---|
| 136 | '.' +
|
|---|
| 137 | String(date.getUTCMilliseconds()).padStart(3, '0')
|
|---|
| 138 |
|
|---|
| 139 | ret += '+00:00'
|
|---|
| 140 | if (isBCYear) ret += ' BC'
|
|---|
| 141 | return ret
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | function normalizeQueryConfig(config, values, callback) {
|
|---|
| 145 | // can take in strings or config objects
|
|---|
| 146 | config = typeof config === 'string' ? { text: config } : config
|
|---|
| 147 | if (values) {
|
|---|
| 148 | if (typeof values === 'function') {
|
|---|
| 149 | config.callback = values
|
|---|
| 150 | } else {
|
|---|
| 151 | config.values = values
|
|---|
| 152 | }
|
|---|
| 153 | }
|
|---|
| 154 | if (callback) {
|
|---|
| 155 | config.callback = callback
|
|---|
| 156 | }
|
|---|
| 157 | return config
|
|---|
| 158 | }
|
|---|
| 159 |
|
|---|
| 160 | // Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|---|
| 161 | const escapeIdentifier = function (str) {
|
|---|
| 162 | return '"' + str.replace(/"/g, '""') + '"'
|
|---|
| 163 | }
|
|---|
| 164 |
|
|---|
| 165 | const escapeLiteral = function (str) {
|
|---|
| 166 | let hasBackslash = false
|
|---|
| 167 | let escaped = "'"
|
|---|
| 168 |
|
|---|
| 169 | if (str == null) {
|
|---|
| 170 | return "''"
|
|---|
| 171 | }
|
|---|
| 172 |
|
|---|
| 173 | if (typeof str !== 'string') {
|
|---|
| 174 | return "''"
|
|---|
| 175 | }
|
|---|
| 176 |
|
|---|
| 177 | for (let i = 0; i < str.length; i++) {
|
|---|
| 178 | const c = str[i]
|
|---|
| 179 | if (c === "'") {
|
|---|
| 180 | escaped += c + c
|
|---|
| 181 | } else if (c === '\\') {
|
|---|
| 182 | escaped += c + c
|
|---|
| 183 | hasBackslash = true
|
|---|
| 184 | } else {
|
|---|
| 185 | escaped += c
|
|---|
| 186 | }
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | escaped += "'"
|
|---|
| 190 |
|
|---|
| 191 | if (hasBackslash === true) {
|
|---|
| 192 | escaped = ' E' + escaped
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | return escaped
|
|---|
| 196 | }
|
|---|
| 197 |
|
|---|
| 198 | module.exports = {
|
|---|
| 199 | prepareValue: function prepareValueWrapper(value) {
|
|---|
| 200 | // this ensures that extra arguments do not get passed into prepareValue
|
|---|
| 201 | // by accident, eg: from calling values.map(utils.prepareValue)
|
|---|
| 202 | return prepareValue(value)
|
|---|
| 203 | },
|
|---|
| 204 | normalizeQueryConfig,
|
|---|
| 205 | escapeIdentifier,
|
|---|
| 206 | escapeLiteral,
|
|---|
| 207 | }
|
|---|