UNPKG

5 kBJavaScriptView Raw
1'use strict'
2/**
3 * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com)
4 * All rights reserved.
5 *
6 * This source code is licensed under the MIT license found in the
7 * README.md file in the root directory of this source tree.
8 */
9
10const crypto = require('crypto')
11
12const defaults = require('./defaults')
13
14function escapeElement(elementRepresentation) {
15 var escaped = elementRepresentation.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
16
17 return '"' + escaped + '"'
18}
19
20// convert a JS array to a postgres array literal
21// uses comma separator so won't work for types like box that use
22// a different array separator.
23function arrayString(val) {
24 var result = '{'
25 for (var i = 0; i < val.length; i++) {
26 if (i > 0) {
27 result = result + ','
28 }
29 if (val[i] === null || typeof val[i] === 'undefined') {
30 result = result + 'NULL'
31 } else if (Array.isArray(val[i])) {
32 result = result + arrayString(val[i])
33 } else if (val[i] instanceof Buffer) {
34 result += '\\\\x' + val[i].toString('hex')
35 } else {
36 result += escapeElement(prepareValue(val[i]))
37 }
38 }
39 result = result + '}'
40 return result
41}
42
43// converts values from javascript types
44// to their 'raw' counterparts for use as a postgres parameter
45// note: you can override this function to provide your own conversion mechanism
46// for complex types, etc...
47var prepareValue = function (val, seen) {
48 if (val instanceof Buffer) {
49 return val
50 }
51 if (ArrayBuffer.isView(val)) {
52 var buf = Buffer.from(val.buffer, val.byteOffset, val.byteLength)
53 if (buf.length === val.byteLength) {
54 return buf
55 }
56 return buf.slice(val.byteOffset, val.byteOffset + val.byteLength) // Node.js v4 does not support those Buffer.from params
57 }
58 if (val instanceof Date) {
59 if (defaults.parseInputDatesAsUTC) {
60 return dateToStringUTC(val)
61 } else {
62 return dateToString(val)
63 }
64 }
65 if (Array.isArray(val)) {
66 return arrayString(val)
67 }
68 if (val === null || typeof val === 'undefined') {
69 return null
70 }
71 if (typeof val === 'object') {
72 return prepareObject(val, seen)
73 }
74 return val.toString()
75}
76
77function prepareObject(val, seen) {
78 if (val && typeof val.toPostgres === 'function') {
79 seen = seen || []
80 if (seen.indexOf(val) !== -1) {
81 throw new Error('circular reference detected while preparing "' + val + '" for query')
82 }
83 seen.push(val)
84
85 return prepareValue(val.toPostgres(prepareValue), seen)
86 }
87 return JSON.stringify(val)
88}
89
90function pad(number, digits) {
91 number = '' + number
92 while (number.length < digits) {
93 number = '0' + number
94 }
95 return number
96}
97
98function dateToString(date) {
99 var offset = -date.getTimezoneOffset()
100
101 var year = date.getFullYear()
102 var isBCYear = year < 1
103 if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
104
105 var ret =
106 pad(year, 4) +
107 '-' +
108 pad(date.getMonth() + 1, 2) +
109 '-' +
110 pad(date.getDate(), 2) +
111 'T' +
112 pad(date.getHours(), 2) +
113 ':' +
114 pad(date.getMinutes(), 2) +
115 ':' +
116 pad(date.getSeconds(), 2) +
117 '.' +
118 pad(date.getMilliseconds(), 3)
119
120 if (offset < 0) {
121 ret += '-'
122 offset *= -1
123 } else {
124 ret += '+'
125 }
126
127 ret += pad(Math.floor(offset / 60), 2) + ':' + pad(offset % 60, 2)
128 if (isBCYear) ret += ' BC'
129 return ret
130}
131
132function dateToStringUTC(date) {
133 var year = date.getUTCFullYear()
134 var isBCYear = year < 1
135 if (isBCYear) year = Math.abs(year) + 1 // negative years are 1 off their BC representation
136
137 var ret =
138 pad(year, 4) +
139 '-' +
140 pad(date.getUTCMonth() + 1, 2) +
141 '-' +
142 pad(date.getUTCDate(), 2) +
143 'T' +
144 pad(date.getUTCHours(), 2) +
145 ':' +
146 pad(date.getUTCMinutes(), 2) +
147 ':' +
148 pad(date.getUTCSeconds(), 2) +
149 '.' +
150 pad(date.getUTCMilliseconds(), 3)
151
152 ret += '+00:00'
153 if (isBCYear) ret += ' BC'
154 return ret
155}
156
157function normalizeQueryConfig(config, values, callback) {
158 // can take in strings or config objects
159 config = typeof config === 'string' ? { text: config } : config
160 if (values) {
161 if (typeof values === 'function') {
162 config.callback = values
163 } else {
164 config.values = values
165 }
166 }
167 if (callback) {
168 config.callback = callback
169 }
170 return config
171}
172
173const md5 = function (string) {
174 return crypto.createHash('md5').update(string, 'utf-8').digest('hex')
175}
176
177// See AuthenticationMD5Password at https://www.postgresql.org/docs/current/static/protocol-flow.html
178const postgresMd5PasswordHash = function (user, password, salt) {
179 var inner = md5(password + user)
180 var outer = md5(Buffer.concat([Buffer.from(inner), salt]))
181 return 'md5' + outer
182}
183
184module.exports = {
185 prepareValue: function prepareValueWrapper(value) {
186 // this ensures that extra arguments do not get passed into prepareValue
187 // by accident, eg: from calling values.map(utils.prepareValue)
188 return prepareValue(value)
189 },
190 normalizeQueryConfig,
191 postgresMd5PasswordHash,
192 md5,
193}