2fd9c77085
* Make Query & NativeQuery implement the promise interface * Fix test * Use older node API for checking listener length * Do not test for promises on node@v0.10.0
131 lines
3.5 KiB
JavaScript
131 lines
3.5 KiB
JavaScript
var EventEmitter = require('events').EventEmitter;
|
|
var util = require('util');
|
|
var utils = require('../utils');
|
|
var NativeResult = require('./result');
|
|
|
|
var NativeQuery = module.exports = function(native) {
|
|
EventEmitter.call(this);
|
|
this.native = native;
|
|
this.text = null;
|
|
this.values = null;
|
|
this.name = null;
|
|
this.callback = null;
|
|
this.state = 'new';
|
|
this._arrayMode = false;
|
|
|
|
//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);
|
|
|
|
NativeQuery.prototype.then = function(callback) {
|
|
return this.promise().then(callback);
|
|
};
|
|
|
|
NativeQuery.prototype.catch = function(callback) {
|
|
return this.promise().catch(callback);
|
|
};
|
|
|
|
NativeQuery.prototype.promise = 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.handleError = function(err) {
|
|
var self = this;
|
|
//copy pq error fields into the error object
|
|
var fields = self.native.pq.resultErrorFields();
|
|
if(fields) {
|
|
for(var key in fields) {
|
|
err[key] = fields[key];
|
|
}
|
|
}
|
|
if(self.callback) {
|
|
self.callback(err);
|
|
} else {
|
|
self.emit('error', err);
|
|
}
|
|
self.state = 'error';
|
|
};
|
|
|
|
NativeQuery.prototype.submit = function(client) {
|
|
this.state = 'running';
|
|
var self = this;
|
|
client.native.arrayMode = this._arrayMode;
|
|
|
|
var after = function(err, rows) {
|
|
client.native.arrayMode = false;
|
|
setImmediate(function() {
|
|
self.emit('_done');
|
|
});
|
|
|
|
//handle possible query error
|
|
if(err) {
|
|
return self.handleError(err);
|
|
}
|
|
|
|
var result = new NativeResult();
|
|
result.addCommandComplete(self.native.pq);
|
|
result.rows = rows;
|
|
|
|
//emit row events for each row in the result
|
|
if(self._emitRowEvents) {
|
|
rows.forEach(function(row) {
|
|
self.emit('row', row, result);
|
|
});
|
|
}
|
|
|
|
|
|
//handle successful result
|
|
self.state = 'end';
|
|
self.emit('end', result);
|
|
if(self.callback) {
|
|
self.callback(null, result);
|
|
}
|
|
};
|
|
|
|
if(process.domain) {
|
|
after = process.domain.bind(after);
|
|
}
|
|
|
|
//named query
|
|
if(this.name) {
|
|
if (this.name.length > 63) {
|
|
console.error('Warning! Postgres only supports 63 characters for query names.');
|
|
console.error('You supplied', this.name, '(', this.name.length, ')');
|
|
console.error('This can cause conflicts and silent errors executing queries');
|
|
}
|
|
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]) {
|
|
return this.native.execute(this.name, values, after);
|
|
}
|
|
//plan the named query the first time, then execute it
|
|
return this.native.prepare(this.name, this.text, values.length, function(err) {
|
|
if(err) return after(err);
|
|
client.namedQueries[self.name] = true;
|
|
return self.native.execute(self.name, values, after);
|
|
});
|
|
}
|
|
else if(this.values) {
|
|
var vals = this.values.map(utils.prepareValue);
|
|
this.native.query(this.text, vals, after);
|
|
} else {
|
|
this.native.query(this.text, after);
|
|
}
|
|
};
|