123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- 'use strict'
- var EventEmitter = require('events').EventEmitter
- var util = require('util')
- var utils = require('../utils')
- var NativeQuery = (module.exports = function (config, values, callback) {
- EventEmitter.call(this)
- config = utils.normalizeQueryConfig(config, values, callback)
- this.text = config.text
- this.values = config.values
- this.name = config.name
- this.callback = config.callback
- this.state = 'new'
- this._arrayMode = config.rowMode === 'array'
- // if the 'row' event is listened for
- // then emit them as they come in
- // without setting singleRowMode to true
- // this has almost no meaning because libpq
- // reads all rows into memory befor returning any
- this._emitRowEvents = false
- this.on(
- 'newListener',
- function (event) {
- if (event === 'row') this._emitRowEvents = true
- }.bind(this)
- )
- })
- util.inherits(NativeQuery, EventEmitter)
- var errorFieldMap = {
- /* eslint-disable quote-props */
- sqlState: 'code',
- statementPosition: 'position',
- messagePrimary: 'message',
- context: 'where',
- schemaName: 'schema',
- tableName: 'table',
- columnName: 'column',
- dataTypeName: 'dataType',
- constraintName: 'constraint',
- sourceFile: 'file',
- sourceLine: 'line',
- sourceFunction: 'routine',
- }
- NativeQuery.prototype.handleError = function (err) {
- // copy pq error fields into the error object
- var fields = this.native.pq.resultErrorFields()
- if (fields) {
- for (var key in fields) {
- var normalizedFieldName = errorFieldMap[key] || key
- err[normalizedFieldName] = fields[key]
- }
- }
- if (this.callback) {
- this.callback(err)
- } else {
- this.emit('error', err)
- }
- this.state = 'error'
- }
- NativeQuery.prototype.then = function (onSuccess, onFailure) {
- return this._getPromise().then(onSuccess, onFailure)
- }
- NativeQuery.prototype.catch = function (callback) {
- return this._getPromise().catch(callback)
- }
- NativeQuery.prototype._getPromise = function () {
- if (this._promise) return this._promise
- this._promise = new Promise(
- function (resolve, reject) {
- this._once('end', resolve)
- this._once('error', reject)
- }.bind(this)
- )
- return this._promise
- }
- NativeQuery.prototype.submit = function (client) {
- this.state = 'running'
- var self = this
- this.native = client.native
- client.native.arrayMode = this._arrayMode
- var after = function (err, rows, results) {
- client.native.arrayMode = false
- setImmediate(function () {
- self.emit('_done')
- })
- // handle possible query error
- if (err) {
- return self.handleError(err)
- }
- // emit row events for each row in the result
- if (self._emitRowEvents) {
- if (results.length > 1) {
- rows.forEach((rowOfRows, i) => {
- rowOfRows.forEach((row) => {
- self.emit('row', row, results[i])
- })
- })
- } else {
- rows.forEach(function (row) {
- self.emit('row', row, results)
- })
- }
- }
- // handle successful result
- self.state = 'end'
- self.emit('end', results)
- if (self.callback) {
- self.callback(null, results)
- }
- }
- if (process.domain) {
- after = process.domain.bind(after)
- }
- // named query
- if (this.name) {
- if (this.name.length > 63) {
- /* eslint-disable no-console */
- console.error('Warning! Postgres only supports 63 characters for query names.')
- console.error('You supplied %s (%s)', this.name, this.name.length)
- console.error('This can cause conflicts and silent errors executing queries')
- /* eslint-enable no-console */
- }
- var values = (this.values || []).map(utils.prepareValue)
- // check if the client has already executed this named query
- // if so...just execute it again - skip the planning phase
- if (client.namedQueries[this.name]) {
- if (this.text && client.namedQueries[this.name] !== this.text) {
- const err = new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`)
- return after(err)
- }
- return client.native.execute(this.name, values, after)
- }
- // plan the named query the first time, then execute it
- return client.native.prepare(this.name, this.text, values.length, function (err) {
- if (err) return after(err)
- client.namedQueries[self.name] = self.text
- return self.native.execute(self.name, values, after)
- })
- } else if (this.values) {
- if (!Array.isArray(this.values)) {
- const err = new Error('Query values must be an array')
- return after(err)
- }
- var vals = this.values.map(utils.prepareValue)
- client.native.query(this.text, vals, after)
- } else {
- client.native.query(this.text, after)
- }
- }
|