utils.js 4.7 KB

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