utils.js 5.5 KB

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