utils.js 5.1 KB

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