'use strict';
var uuid = require( 'uuid' );
var Statement = require( './statement' );
/**
* Class representing a context which can be used to execute SQL statements in
* chronological order and use transactions safely within the scope of the
* context.
*
* @constructor
* @param {Pool} - Connection pool to be used for this context
*/
var Context = function ( pool ) {
// Privileged data
this.$ = {
conn : false,
id : uuid.v4(),
pool : pool,
stmts : {
begin : false,
commit : false,
rollback : false
},
transaction : false,
};
// Reserve a connection for this context
this.$.conn = this.$.pool.reserve( this.id() );
};
/**
* Begin a transaction for this context
*
* @return {Promise.<string, Error>} - A promise which resolves after openning a
* transaction or an Error if rejected.
*/
Context.prototype.begin = function () {
var self = this;
var stmt;
if (! this.$.stmts.begin ) {
stmt = new Statement( this.$.conn, { context : this.id(), id : 'begin' } );
stmt = this.$.stmts.begin = stmt.prepare( 'begin;' );
} else {
stmt = this.$.stmts.begin;
}
return stmt
.then( function ( stmt ) {
self.$.transaction = true;
return stmt.exec();
} )
.then( function ( cursor ) {
return cursor.close();
} );
};
/**
* Commit a transaction opened for this context
*
* @return {Promise.<string, Error>} - A promise which resolves after committing a
* transaction or an Error if rejected.
*/
Context.prototype.commit = function () {
var self = this;
var stmt;
if (! this.$.stmts.commit ) {
stmt = new Statement( this.$.conn, { context : this.id(), id : 'commit' } );
stmt = this.$.stmts.commit = stmt.prepare( 'commit;' );
} else {
stmt = this.$.stmts.commit;
}
return stmt
.then( function ( stmt ) {
self.$.transaction = false;
return stmt.exec();
} )
.then( function ( cursor ) {
return cursor.close();
} );
};
/**
* Rollback any open transactions and end the context by releasing the connection
* used back to the pool.
*
* @return {Promise<string, Error>} - A promise to a context ID string which
* resolved after the context is ended or an Error if rejected.
*/
Context.prototype.end = function () {
var self = this;
var promise;
if ( self.$.transaction ) {
promise = self.rollback();
} else {
promise = Promise.resolve();
}
if ( self.$.stmts.begin ) {
promise = promise
.then( function () {
return self.$.stmts.begin.then( function ( stmt ) {
return stmt.free();
} );
} );
}
if ( self.$.stmts.commit ) {
promise = promise
.then( function () {
return self.$.stmts.commit.then( function ( stmt ) {
return stmt.free();
} );
} );
}
if ( self.$.stmts.rollback ) {
promise = promise
.then( function () {
return self.$.stmts.rollback.then( function ( stmt ) {
return stmt.free();
} );
} );
}
return promise
.then( function () {
return self.$.pool.close( self.id() );
} )
.then( function () {
return self.id();
} );
};
/**
* Return the context ID
*
* @return {string} - Context ID.
*/
Context.prototype.id = function () {
return this.$.id;
};
/**
* Prepare a statement which is only valid to be executed within this context
*
* @param {string} sql - SQL statement to prepare
* @return {Promise.<Statement, Error>} - A promise to a statement object or an
* Error if rejected.
*/
Context.prototype.prepare = function ( sql ) {
var stmt = new Statement( this.$.conn, { context : this.id() } );
return stmt.prepare( sql );
};
/**
* Run a query within this context
*
* @param {string} sql - SQL query to run
* @return {Promise.<Cursor, Error>} - A promise to a results cursor or an Error
* if rejected.
*/
Context.prototype.query = function ( sql ) {
var stmt = new Statement( this.$.conn, { context : this.id(), reusable : false } );
return stmt.prepare( sql )
.then( function ( stmt ) {
return stmt.exec();
} );
};
/**
* Rollback a transaction opened for this context
*
* @return {Promise.<string, Error>} - A promise which resolves after rolling-back a
* transaction or an Error if rejected.
*/
Context.prototype.rollback = function () {
var self = this;
var stmt;
if (! this.$.stmts.rollback ) {
stmt = new Statement( this.$.conn, { context : this.id(), id : 'rollback' } );
stmt = this.$.stmts.rollback = stmt.prepare( 'rollback;' );
} else {
stmt = this.$.stmts.rollback;
}
return stmt
.then( function ( stmt ) {
self.$.transaction = false;
return stmt.exec();
} )
.then( function ( cursor ) {
return cursor.close();
} );
};
module.exports = Context;