utils.js 4.8 KB

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