pg.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. const md5 = require('md5.js')
  2. function syncMessage () {
  3. const len = 5
  4. const buf = new ArrayBuffer(len)
  5. const dv = new DataView(buf)
  6. dv.setUint8(0, 83)
  7. dv.setUint32(1, 4)
  8. return buf
  9. }
  10. function startupMessage ({ user, database, parameters = [] }) {
  11. let len = 8 + 4 + 1 + user.length + 1 + 8 + 1 + database.length + 2
  12. for (let i = 0; i < parameters.length; i++) {
  13. const { name, value } = parameters[i]
  14. len += (name.length + 1 + value.length + 1)
  15. }
  16. const buf = new ArrayBuffer(len)
  17. const dv = new DataView(buf)
  18. let off = 0
  19. dv.setInt32(0, 0)
  20. off += 4
  21. // 0x00030000 = 3.0
  22. dv.setInt32(4, 196608)
  23. off += 4
  24. off += buf.writeString('user', off)
  25. dv.setUint8(off++, 0)
  26. off += buf.writeString(user, off)
  27. dv.setUint8(off++, 0)
  28. off += buf.writeString('database', off)
  29. dv.setUint8(off++, 0)
  30. off += buf.writeString(database, off)
  31. dv.setUint8(off++, 0)
  32. for (let i = 0; i < parameters.length; i++) {
  33. const { name, value } = parameters[i]
  34. off += buf.writeString(name, off)
  35. dv.setUint8(off++, 0)
  36. off += buf.writeString(value, off)
  37. dv.setUint8(off++, 0)
  38. }
  39. dv.setUint8(off++, 0)
  40. dv.setInt32(0, off)
  41. return buf
  42. }
  43. function md5AuthMessage ({ user, pass, salt }) {
  44. const token = `${pass}${user}`
  45. let hash = md5(token)
  46. const plain = new ArrayBuffer(36)
  47. plain.writeString(`md5${hash}`, 0)
  48. const plain2 = new ArrayBuffer(36)
  49. plain2.copyFrom(plain, 0, 32, 3)
  50. plain2.copyFrom(salt, 32, 4)
  51. hash = `md5${md5(plain2)}`
  52. const len = hash.length + 5
  53. let off = 0
  54. const buf = new ArrayBuffer(len + 1)
  55. const dv = new DataView(buf)
  56. dv.setUint8(off++, 112)
  57. dv.setUint32(off, len)
  58. off += 4
  59. off += buf.writeString(hash, off)
  60. dv.setUint8(off++, 0)
  61. return buf
  62. }
  63. function createParser (buf) {
  64. let nextRow = 0
  65. let parseNext = 0
  66. let parameters = {}
  67. const query = { start: 0, end: 0, rows: 0, running: false }
  68. if (freeList.length) return freeList.shift()
  69. function onDataRow (len, off) {
  70. // D = DataRow
  71. nextRow++
  72. return off + len - 4
  73. }
  74. function onCommandComplete (len, off) {
  75. // C = CommandComplete
  76. query.end = off
  77. query.rows = nextRow
  78. query.running = false
  79. off += len - 4
  80. nextRow = 0
  81. parser.onMessage()
  82. return off
  83. }
  84. function onRowDescripton (len, off) {
  85. // T = RowDescription
  86. const fieldCount = dv.getInt16(off)
  87. off += 2
  88. fields.length = 0
  89. for (let i = 0; i < fieldCount; i++) {
  90. const name = readCString(buf, u8, off)
  91. off += name.length + 1
  92. const tid = dv.getInt32(off)
  93. off += 4
  94. const attrib = dv.getInt16(off)
  95. off += 2
  96. const oid = dv.getInt32(off)
  97. off += 4
  98. const size = dv.getInt16(off)
  99. off += 2
  100. const mod = dv.getInt32(off)
  101. off += 4
  102. const format = dv.getInt16(off)
  103. off += 2
  104. fields.push({ name, tid, attrib, oid, size, mod, format })
  105. }
  106. parser.onMessage()
  107. return off
  108. }
  109. function onAuthenticationOk (len, off) {
  110. // R = AuthenticationOk
  111. const method = dv.getInt32(off)
  112. off += 4
  113. if (method === constants.AuthenticationMD5Password) {
  114. parser.salt = buf.slice(off, off + 4)
  115. off += 4
  116. parser.onMessage()
  117. }
  118. return off
  119. }
  120. function onErrorResponse (len, off) {
  121. // E = ErrorResponse
  122. errors.length = 0
  123. let fieldType = u8[off++]
  124. while (fieldType !== 0) {
  125. const val = readCString(buf, u8, off)
  126. errors.push({ type: fieldType, val })
  127. off += (val.length + 1)
  128. fieldType = u8[off++]
  129. }
  130. parser.onMessage()
  131. return off
  132. }
  133. function onParameterStatus (len, off) {
  134. // S = ParameterStatus
  135. const key = readCString(buf, u8, off)
  136. off += (key.length + 1)
  137. const val = readCString(buf, u8, off)
  138. off += val.length + 1
  139. parameters[key] = val
  140. return off
  141. }
  142. function onParameterDescription (len, off) {
  143. // t = ParameterDescription
  144. const nparams = dv.getInt16(off)
  145. parser.params = []
  146. off += 2
  147. for (let i = 0; i < nparams; i++) {
  148. parser.params.push(dv.getUint32(off))
  149. off += 4
  150. }
  151. return off
  152. }
  153. function onParseComplete (len, off) {
  154. // 1 = ParseComplete
  155. off += len - 4
  156. parser.onMessage()
  157. return off
  158. }
  159. function onBindComplete (len, off) {
  160. // 2 = BindComplete
  161. off += len - 4
  162. parser.onMessage()
  163. query.rows = 0
  164. query.start = query.end = off
  165. query.running = true
  166. return off
  167. }
  168. function onReadyForQuery (len, off) {
  169. // Z = ReadyForQuery
  170. parser.status = u8[off]
  171. parser.onMessage()
  172. off += len - 4
  173. return off
  174. }
  175. function onBackendKeyData (len, off) {
  176. // K = BackendKeyData
  177. parser.pid = dv.getUint32(off)
  178. off += 4
  179. parser.key = dv.getUint32(off)
  180. off += 4
  181. parser.onMessage()
  182. return off
  183. }
  184. function parse (bytesRead) {
  185. let type
  186. let len
  187. let off = parseNext
  188. const end = buf.offset + bytesRead
  189. while (off < end) {
  190. const remaining = end - off
  191. let want = 5
  192. if (remaining < want) {
  193. if (byteLength - off < 1024) {
  194. if (query.running) {
  195. const queryLen = off - query.start + remaining
  196. buf.copyFrom(buf, 0, queryLen, query.start)
  197. buf.offset = queryLen
  198. parseNext = off - query.start
  199. query.start = 0
  200. return
  201. }
  202. buf.copyFrom(buf, 0, remaining, off)
  203. buf.offset = remaining
  204. parseNext = 0
  205. return
  206. }
  207. buf.offset = off + remaining
  208. parseNext = off
  209. return
  210. }
  211. type = parser.type = dv.getUint8(off)
  212. len = parser.len = dv.getUint32(off + 1)
  213. want = len + 1
  214. if (remaining < want) {
  215. if (byteLength - off < 1024) {
  216. if (query.running) {
  217. const queryLen = off - query.start + remaining
  218. buf.copyFrom(buf, 0, queryLen, query.start)
  219. buf.offset = queryLen
  220. parseNext = off - query.start
  221. query.start = 0
  222. return
  223. }
  224. buf.copyFrom(buf, 0, remaining, off)
  225. buf.offset = remaining
  226. parseNext = 0
  227. return
  228. }
  229. buf.offset = off + remaining
  230. parseNext = off
  231. return
  232. }
  233. off += 5
  234. off = (V[type] || V[0])(len, off)
  235. }
  236. parseNext = buf.offset = 0
  237. }
  238. function getResult () {
  239. return readCString(buf, u8, parseNext)
  240. }
  241. function onDefault (len, off) {
  242. off += len - 4
  243. parser.onMessage()
  244. return off
  245. }
  246. function free () {
  247. parser.fields.length = 0
  248. parser.errors.length = 0
  249. parameters = parser.parameters = {}
  250. nextRow = 0
  251. parseNext = 0
  252. query.start = query.end = query.rows = 0
  253. query.running = false
  254. freeList.push(parser)
  255. }
  256. const { messageTypes } = constants
  257. const dv = new DataView(buf)
  258. const u8 = new Uint8Array(buf)
  259. const byteLength = buf.byteLength
  260. const fields = []
  261. const errors = []
  262. const V = {
  263. [messageTypes.AuthenticationOk]: onAuthenticationOk,
  264. [messageTypes.ErrorResponse]: onErrorResponse,
  265. [messageTypes.RowDescription]: onRowDescripton,
  266. [messageTypes.CommandComplete]: onCommandComplete,
  267. [messageTypes.ParseComplete]: onParseComplete,
  268. [messageTypes.BindComplete]: onBindComplete,
  269. [messageTypes.ReadyForQuery]: onReadyForQuery,
  270. [messageTypes.BackendKeyData]: onBackendKeyData,
  271. [messageTypes.ParameterStatus]: onParameterStatus,
  272. [messageTypes.ParameterDescription]: onParameterDescription,
  273. [messageTypes.DataRow]: onDataRow,
  274. 0: onDefault
  275. }
  276. const parser = {
  277. buf,
  278. dv,
  279. fields,
  280. parameters,
  281. type: 0,
  282. len: 0,
  283. errors,
  284. getResult,
  285. parse,
  286. free,
  287. query
  288. }
  289. return parser
  290. }
  291. function readCString (buf, u8, off) {
  292. const start = off
  293. while (u8[off] !== 0) off++
  294. return buf.readString(off - start, start)
  295. }
  296. function getPGError (errors) {
  297. return errors.filter(v => v.type === 77)[0].val
  298. }
  299. const constants = {
  300. AuthenticationMD5Password: 5,
  301. fieldTypes: {
  302. INT4OID: 23,
  303. VARCHAROID: 1043
  304. },
  305. messageTypes: {
  306. AuthenticationOk: 82,
  307. ErrorResponse: 69,
  308. RowDescription: 84,
  309. CommandComplete: 67,
  310. ParseComplete: 49,
  311. BindComplete: 50,
  312. ReadyForQuery: 90,
  313. BackendKeyData: 75,
  314. ParameterStatus: 83,
  315. ParameterDescription: 116,
  316. DataRow: 68,
  317. NoData: 110
  318. }
  319. }
  320. const freeList = []
  321. module.exports = { createParser, syncMessage, startupMessage, md5AuthMessage, getPGError, constants }