libpq
Version:
Low-level native bindings to PostgreSQL libpq
412 lines (348 loc) • 14 kB
JavaScript
var PQ = (module.exports = require('bindings')('addon.node').PQ);
var assert = require('assert');
//print out the include dir
//if you want to include this in a binding.gyp file
if (!module.parent) {
var path = require('path');
console.log(path.normalize(__dirname + '/src'));
}
var EventEmitter = require('events').EventEmitter;
Object.setPrototypeOf(PQ.prototype, EventEmitter.prototype)
//SYNC connects to the server
//throws an exception in the event of a connection error
PQ.prototype.connectSync = function (paramString) {
this.connected = true;
if (!paramString) {
paramString = '';
}
var connected = this.$connectSync(paramString);
if (!connected) {
var err = new Error(this.errorMessage());
this.finish();
throw err;
}
};
//connects async using a background thread
//calls the callback with an error if there was one
PQ.prototype.connect = function (paramString, cb) {
this.connected = true;
if (typeof paramString == 'function') {
cb = paramString;
paramString = '';
}
if (!paramString) {
paramString = '';
}
assert(cb, 'Must provide a connection callback');
if (process.domain) {
cb = process.domain.bind(cb);
}
this.$connect(paramString, cb);
};
PQ.prototype.errorMessage = PQ.prototype.$getLastErrorMessage;
//returns an int for the fd of the socket
PQ.prototype.socket = PQ.prototype.$socket;
// return server version number e.g. 90300
PQ.prototype.serverVersion = PQ.prototype.$serverVersion;
// returns the current in-transaction status of the server
// 0=IDLE, 1=ACTIVE, 2=INTRANS, 3=INERROR, 4=UNKNOWN
PQ.prototype.transactionStatus = function () {
return this.$transactionStatus();
};
//finishes the connection & closes it
PQ.prototype.finish = function () {
this.connected = false;
this.$finish();
};
////SYNC executes a plain text query
//immediately stores the results within the PQ object for consumption with
//ntuples, getvalue, etc...
//returns false if there was an error
//consume additional error details via PQ#errorMessage & friends
PQ.prototype.exec = function (commandText) {
if (!commandText) {
commandText = '';
}
this.$exec(commandText);
};
//SYNC executes a query with parameters
//immediately stores the results within the PQ object for consumption with
//ntuples, getvalue, etc...
//returns false if there was an error
//consume additional error details via PQ#errorMessage & friends
PQ.prototype.execParams = function (commandText, parameters) {
if (!commandText) {
commandText = '';
}
if (!parameters) {
parameters = [];
}
assert(Array.isArray(parameters), 'Parameters must be an array');
this.$execParams(commandText, parameters);
};
//SYNC prepares a named query and stores the result
//immediately stores the results within the PQ object for consumption with
//ntuples, getvalue, etc...
//returns false if there was an error
//consume additional error details via PQ#errorMessage & friends
PQ.prototype.prepare = function (statementName, commandText, nParams) {
assert.equal(arguments.length, 3, 'Must supply 3 arguments');
if (!statementName) {
statementName = '';
}
if (!commandText) {
commandText = '';
}
nParams = Number(nParams) || 0;
this.$prepare(statementName, commandText, nParams);
};
//SYNC executes a named, prepared query and stores the result
//immediately stores the results within the PQ object for consumption with
//ntuples, getvalue, etc...
//returns false if there was an error
//consume additional error details via PQ#errorMessage & friends
PQ.prototype.execPrepared = function (statementName, parameters) {
if (!statementName) {
statementName = '';
}
if (!parameters) {
parameters = [];
}
assert(Array.isArray(parameters), 'Parameters must be an array');
this.$execPrepared(statementName, parameters);
};
//SYNC describes a named, prepared query and stores the result
//immediately stores the results within the PQ object for consumption with
//nparams, paramtype, nfields, etc...
PQ.prototype.describePrepared = function(statementName) {
if(!statementName) {
statementName = '';
}
this.$describePrepared(statementName);
};
//send a command to begin executing a query in async mode
//returns true if sent, or false if there was a send failure
PQ.prototype.sendQuery = function (commandText) {
if (!commandText) {
commandText = '';
}
return this.$sendQuery(commandText);
};
//send a command to begin executing a query with parameters in async mode
//returns true if sent, or false if there was a send failure
PQ.prototype.sendQueryParams = function (commandText, parameters) {
if (!commandText) {
commandText = '';
}
if (!parameters) {
parameters = [];
}
assert(Array.isArray(parameters), 'Parameters must be an array');
return this.$sendQueryParams(commandText, parameters);
};
//send a command to prepare a named query in async mode
//returns true if sent, or false if there was a send failure
PQ.prototype.sendPrepare = function (statementName, commandText, nParams) {
assert.equal(arguments.length, 3, 'Must supply 3 arguments');
if (!statementName) {
statementName = '';
}
if (!commandText) {
commandText = '';
}
nParams = Number(nParams) || 0;
return this.$sendPrepare(statementName, commandText, nParams);
};
//send a command to execute a named query in async mode
//returns true if sent, or false if there was a send failure
PQ.prototype.sendQueryPrepared = function (statementName, parameters) {
if (!statementName) {
statementName = '';
}
if (!parameters) {
parameters = [];
}
assert(Array.isArray(parameters), 'Parameters must be an array');
return this.$sendQueryPrepared(statementName, parameters);
};
//'pops' a result out of the buffered
//response data read during async command execution
//and stores it on the c/c++ object so you can consume
//the data from it. returns true if there was a pending result
//or false if there was no pending result. if there was no pending result
//the last found result is not overwritten so you can call getResult as many
//times as you want, and you'll always have the last available result for consumption
PQ.prototype.getResult = PQ.prototype.$getResult;
//returns a text of the enum associated with the result
//usually just PGRES_COMMAND_OK or PGRES_FATAL_ERROR
PQ.prototype.resultStatus = PQ.prototype.$resultStatus;
PQ.prototype.resultErrorMessage = PQ.prototype.$resultErrorMessage;
PQ.prototype.resultErrorFields = PQ.prototype.$resultErrorFields;
//free the memory associated with a result
//this is somewhat handled for you within the c/c++ code
//by never allowing the code to 'leak' a result. still,
//if you absolutely want to free it yourself, you can use this.
PQ.prototype.clear = PQ.prototype.$clear;
//returns the number of tuples (rows) in the result set
PQ.prototype.ntuples = PQ.prototype.$ntuples;
//returns the number of fields (columns) in the result set
PQ.prototype.nfields = PQ.prototype.$nfields;
//returns the name of the field (column) at the given offset
PQ.prototype.fname = PQ.prototype.$fname;
//returns the Oid of the table of the field at the given offset
PQ.prototype.ftable = PQ.prototype.$ftable;
//returns the column number (within its table) of the field at the given offset
PQ.prototype.ftablecol = PQ.prototype.$ftablecol;
//returns the Oid of the type for the given field
PQ.prototype.ftype = PQ.prototype.$ftype;
//returns a text value at the given row/col
//if the value is null this still returns empty string
//so you need to use PQ#getisnull to determine
PQ.prototype.getvalue = PQ.prototype.$getvalue;
//returns true/false if the value is null
PQ.prototype.getisnull = PQ.prototype.$getisnull;
//returns the number of parameters of a prepared statement
PQ.prototype.nparams = PQ.prototype.$nparams;
//returns the data type of the indicated statement parameter (starting from 0)
PQ.prototype.paramtype = PQ.prototype.$paramtype;
//returns the status of the command
PQ.prototype.cmdStatus = PQ.prototype.$cmdStatus;
//returns the tuples in the command
PQ.prototype.cmdTuples = PQ.prototype.$cmdTuples;
//starts the 'read ready' libuv socket listener.
//Once the socket becomes readable, the PQ instance starts
//emitting 'readable' events. Similar to how node's readable-stream
//works except to clear the SELECT() notification you need to call
//PQ#consumeInput instead of letting node pull the data off the socket
//http://www.postgresql.org/docs/9.1/static/libpq-async.html
PQ.prototype.startReader = function () {
assert(this.connected, 'Must be connected to start reader');
this.$startRead();
};
//suspends the libuv socket 'read ready' listener
PQ.prototype.stopReader = PQ.prototype.$stopRead;
PQ.prototype.writable = function (cb) {
assert(this.connected, 'Must be connected to start writer');
this.$startWrite();
return this.once('writable', cb);
};
//returns boolean - false indicates an error condition
//e.g. a failure to consume input
PQ.prototype.consumeInput = PQ.prototype.$consumeInput;
//returns true if PQ#getResult would cause
//the process to block waiting on results
//false indicates PQ#getResult can be called
//with an assurance of not blocking
PQ.prototype.isBusy = PQ.prototype.$isBusy;
//toggles the socket blocking on outgoing writes
PQ.prototype.setNonBlocking = function (truthy) {
return this.$setNonBlocking(truthy ? 1 : 0);
};
//returns true if the connection is non-blocking on writes, otherwise false
//note: connection is always non-blocking on reads if using the send* methods
PQ.prototype.isNonBlocking = PQ.prototype.$isNonBlocking;
//returns 1 if socket is not write-ready
//returns 0 if all data flushed to socket
//returns -1 if there is an error
PQ.prototype.flush = PQ.prototype.$flush;
//escapes a literal and returns the escaped string
//I'm not 100% sure this doesn't do any I/O...need to check that
PQ.prototype.escapeLiteral = function (input) {
if (!input) return input;
return this.$escapeLiteral(input);
};
PQ.prototype.escapeIdentifier = function (input) {
if (!input) return input;
return this.$escapeIdentifier(input);
};
//Checks for any notifications which may have arrivied
//and returns them as a javascript object: {relname: 'string', extra: 'string', be_pid: int}
//if there are no pending notifications this returns undefined
PQ.prototype.notifies = PQ.prototype.$notifies;
//Sends a buffer of binary data to the server
//returns 1 if the command was sent successfully
//returns 0 if the command would block (use PQ#writable here if so)
//returns -1 if there was an error
PQ.prototype.putCopyData = function (buffer) {
assert(buffer instanceof Buffer);
return this.$putCopyData(buffer);
};
//Sends a command to 'finish' the copy
//if an error message is passed, it will be sent to the
//backend and signal a request to cancel the copy in
//returns 1 if sent succesfully
//returns 0 if the command would block
//returns -1 if there was an error
PQ.prototype.putCopyEnd = function (errorMessage) {
if (errorMessage) {
return this.$putCopyEnd(errorMessage);
}
return this.$putCopyEnd();
};
//Gets a buffer of data from a copy out command
//if async is passed as true it will not block waiting
//for the result, otherwise this will BLOCK for a result.
//returns a buffer if successful
//returns 0 if copy is still in process (async only)
//returns -1 if the copy is done
//returns -2 if there was an error
PQ.prototype.getCopyData = function (async) {
return this.$getCopyData(!!async);
};
PQ.prototype.cancel = PQ.prototype.$cancel;
// Pipeline mode functions (PostgreSQL 14+)
// These functions are only available if compiled against PostgreSQL 14 or later
//Enters pipeline mode on the connection
//Returns true if successful, false if failed
//Pipeline mode allows sending multiple queries without waiting for results
PQ.prototype.enterPipelineMode = function () {
if (typeof this.$enterPipelineMode !== 'function') {
throw new Error('Pipeline mode is not supported. Requires PostgreSQL 14+ client libraries.');
}
return this.$enterPipelineMode();
};
//Exits pipeline mode on the connection
//Returns true if successful, false if failed
//Can only exit pipeline mode when the queue is empty and no pending results
PQ.prototype.exitPipelineMode = function () {
if (typeof this.$exitPipelineMode !== 'function') {
throw new Error('Pipeline mode is not supported. Requires PostgreSQL 14+ client libraries.');
}
return this.$exitPipelineMode();
};
//Returns the current pipeline status
//0 = PQ_PIPELINE_OFF (not in pipeline mode)
//1 = PQ_PIPELINE_ON (in pipeline mode)
//2 = PQ_PIPELINE_ABORTED (pipeline aborted due to error)
PQ.prototype.pipelineStatus = function () {
if (typeof this.$pipelineStatus !== 'function') {
throw new Error('Pipeline mode is not supported. Requires PostgreSQL 14+ client libraries.');
}
return this.$pipelineStatus();
};
//Sends a sync message in pipeline mode
//This marks a synchronization point - the server will send results
//for all queries up to this point before processing further queries
//Returns true if successful, false if failed
PQ.prototype.pipelineSync = function () {
if (typeof this.$pipelineSync !== 'function') {
throw new Error('Pipeline mode is not supported. Requires PostgreSQL 14+ client libraries.');
}
return this.$pipelineSync();
};
//Sends a request for the server to flush its output buffer
//Returns true if successful, false if failed
PQ.prototype.sendFlushRequest = function () {
if (typeof this.$sendFlushRequest !== 'function') {
throw new Error('Pipeline mode is not supported. Requires PostgreSQL 14+ client libraries.');
}
return this.$sendFlushRequest();
};
//Check if pipeline mode is supported (compiled against PostgreSQL 14+)
PQ.prototype.pipelineModeSupported = function () {
return typeof this.$enterPipelineMode === 'function';
};
// Pipeline status constants
PQ.PIPELINE_OFF = 0;
PQ.PIPELINE_ON = 1;
PQ.PIPELINE_ABORTED = 2;