Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@mysql/xdevapi

Package Overview
Dependencies
Maintainers
4
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mysql/xdevapi - npm Package Compare versions

Comparing version 8.0.7 to 8.0.8

lib/DevAPI/Binding.js

13

CHANGES.txt

@@ -11,2 +11,15 @@ =====================================================

v8.0.8
======
- Add support for `collection.find()` and `table.select()` row locking
- Add document-specific CRUD methods (`replaceOne()`, `addOrReplaceOne()`, `getOne()` and `removeOne()`)
- Add support for automatic or manual selection of authentication mechanisms
- Remove some dead or non-supported feature code
- Fix some flaky test cases and other some small issues in the test suite
- Refactor the database entity and operation architecture
- Add support for the SQL "IN" operator for matching values against argument lists
- Add support for using the "IN" operator to check if values exist in arrays or objects
- Fix a few pressing bugs on the existing expression parser (empty quoted strings, `NULL`, negation, zeros)
v8.0.7

@@ -13,0 +26,0 @@ ======

10

index.js

@@ -74,6 +74,10 @@ /*

const base = parseUri(properties.getUri());
delete base.endpoints;
const configuration = Object.assign({}, base, overrides, {
endpoints: [{
host: overrides.host || base.endpoints[0].host,
port: overrides.port || base.endpoints[0].port,
socket: overrides.socket || base.endpoints[0].socket
}]
});
const configuration = Object.assign({}, base, overrides);
return createSession(configuration);

@@ -80,0 +84,0 @@ }

@@ -39,3 +39,3 @@ /*

this._user = properties.dbUser;
this._password = properties.dbPassword;
this._password = properties.dbPassword || '';
}

@@ -42,0 +42,0 @@

@@ -29,143 +29,287 @@ /*

const CollectionAdd = require('./CollectionAdd');
const CollectionFind = require('./CollectionFind');
const CollectionModify = require('./CollectionModify');
const CollectionRemove = require('./CollectionRemove');
const DatabaseObject = require('./DatabaseObject');
const LinkOperations = require('./LinkOperations');
const Table = require('./Table');
const collectionAdd = require('./CollectionAdd');
const collectionFind = require('./CollectionFind');
const collectionModify = require('./CollectionModify');
const collectionRemove = require('./CollectionRemove');
const databaseObject = require('./DatabaseObject');
const linking = require('./Linking');
const parseFlexibleParamList = require('./Util/parseFlexibleParamList');
const util = require('util');
const table = require('./Table');
const escapeQuotes = require('./Util/escapeQuotes');
/**
* Collection object
*
* Usually you shouldn't create an instance of this but ask a Schema for it
*
* @param {Session} session
* @param {Schema} schema
* @param {String} collection
* @param {String} alias
* @param {object} links
* @constructor
* @extends DatabaseObject
* Collection factory.
* @module Collection
* @mixes DatabaseObject
* @mixes Linking
*/
function Collection (session, schema, collection, alias, links) {
DatabaseObject.call(this, session, schema);
this._collection = collection;
this._links = links || {};
this._alias = alias;
}
/**
* @private
* @alias module:Collection
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} name - collection name
* @param {string} alias - collection alias
* @param {Object} links - collection join links
* @returns {Collection}
*/
function Collection (session, schema, name, alias, links) {
const state = Object.assign({}, { links: {} }, { alias, links, name, schema });
module.exports = Collection;
return Object.assign({}, databaseObject(session), linking(), {
/**
* Literal object or JSON counterpart.
* @typedef {Object|string} DocumentOrJSON
* @global
* @example
* // literal object
* { foo: 'bar' }
* // JSON string
* '{ "foo": "bar" }'
*/
util.inherits(Collection, DatabaseObject);
LinkOperations.applyTo(Collection);
/**
* Create an operation to add one or more documents to the collection.
* @function
* @name module:Collection#add
* @param {...DocumentOrJSON|DocumentOrJSON[]} expr - object with document data
* @throws {Error} When the input type is invalid.
* @example
* // arguments as single documents
* collection.add({ foo: 'baz' }, { bar: 'qux' })
*
* // array of documents
* collection.add([{ foo: 'baz' }, { bar: 'qux' }])
* @returns {CollectionAdd} The operation instance.
*/
add () {
const documents = parseFlexibleParamList(Array.prototype.slice.call(arguments));
/**
* Get the name of this collection
* @returns {string}
*/
Collection.prototype.getName = function () {
return this._collection;
};
return collectionAdd(this.getSession(), this.getSchema(), this.getName(), documents);
},
/**
* Verifies this collection exists
* @returns {Promise<boolean>}
*/
Collection.prototype.existsInDatabase = function () {
const args = [this._schema.getName(), this._collection];
let status = false;
/**
* Create or replace a document with the given id.
* @function
* @name module:Collection#addOrReplaceOne
* @param {string} id - document id
* @param {Object} data - document properties
* @example
* collection.addOrReplaceOne('foo', { prop1: 'bar', prop2: 'baz' })
* @returns {Promise.<Result>} A promise that resolves to the operation result.
*/
addOrReplaceOne (id, data) {
const doc = Object.assign({}, data, { _id: id });
return this._session._client
.sqlStmtExecute('list_objects', args, (found) => { status = !!found.length; }, null, 'xplugin')
.then(() => status);
};
return collectionAdd(this.getSession(), this.getSchema(), this.getName(), [doc], { upsert: true }).execute();
},
/**
* Find documents from a collection
* @param {String} expr Expression
* @returns {CollectionFind}
*/
Collection.prototype.find = function (expr) {
return new CollectionFind(this._session, this._schema, this._collection, expr);
};
/**
* Set alias for a linking operation using the collection.
* @function
* @name module:Collection#as
* @param {string} alias - new collection alias
* @returns {Collection} The entity instance.
*/
as (alias) {
return Collection(this.getSession(), this.getSchema(), this.getName(), alias, this.getLinks());
},
/**
* Add documents
* @param {...Object|Object[]} document - object with document data
* @throws {Error} When the input type is invalid.
* @example
* // arguments as single documents
* collection.add({ foo: 'baz' }, { bar: 'qux' })
*
* // array of documents
* collection.add([{ foo: 'baz' }, { bar: 'qux' }])
* @returns {CollectionAdd}
*/
Collection.prototype.add = function () {
const documents = parseFlexibleParamList(Array.prototype.slice.call(arguments));
/**
* Retrieve the total number of documents in the collection.
* @function
* @name module:Table#count
* @returns {Promise.<number>}
*/
// TODO(Rui): extract method into a proper aspect (to be used on Collection and Table).
count () {
const schema = table.escapeIdentifier(this.getSchema().getName());
const collection = table.escapeIdentifier(this.getName());
return new CollectionAdd(this._session, this._schema, this._collection, documents);
};
let count = 0;
/**
* Run modify operations
* @param {string} expr Expression
* @example
* // update all documents in a collection
* collection.modify('true').set('name', 'bar')
*
* // update documents that match a given condition
* collection.modify('$.name == "foo"').set('name', 'bar')
* @returns {CollectionModify}
*/
Collection.prototype.modify = function (expr) {
return new CollectionModify(this._session, this._schema, this._collection, expr);
};
return this
.getSession()
._client
.sqlStmtExecute(`SELECT COUNT(*) FROM ${schema}.${collection}`, [], row => { count = row[0]; })
.then(() => count);
},
/**
* Create an operation to remove documents from a collection.
* @param {string} expr Expression
* @example
* // remove all documents from a collection
* collection.remove('true')
*
* // remove documents that match a given condition
* collection.remove('$.name == "foobar"')
* @returns {CollectionRemove}
*/
Collection.prototype.remove = function (expr) {
return new CollectionRemove(this._session, this._schema, this._collection, expr);
};
/**
* Check if this collection exists in the database.
* @function
* @name module:Collection#existsInDatabase
* @returns {Promise.<boolean>}
*/
// TODO(Rui): extract method into a proper aspect (to be used on Collection, Schema and Table).
existsInDatabase () {
const args = [this.getSchema().getName(), this.getName()];
let status = false;
/**
* Get number of documents in this Collection
*
* @returns {Promise.<Number>}
*/
Collection.prototype.count = function () {
const schema = Table.escapeIdentifier(this._schema.getName());
const collection = Table.escapeIdentifier(this._collection);
return this
.getSession()
._client
.sqlStmtExecute('list_objects', args, (found) => { status = !!found.length; }, null, 'xplugin')
.then(() => status);
},
let count = 0;
/**
* Expression that establishes the filtering criteria.
* @typedef {string} SearchConditionStr
* @global
* @see {@link https://dev.mysql.com/doc/x-devapi-userguide/en/crud-ebnf-other-definitions.html|X DevAPI User Guide}
*/
return this._session._client
.sqlStmtExecute(`SELECT COUNT(*) FROM ${schema}.${collection}`, [], row => { count = row[0]; })
.then(() => count);
};
/**
* Create an operation to find documents in the collection.
* @function
* @name module:Collection#find
* @param {SearchConditionStr} expr - filtering criteria
* @returns {CollectionFind} The operation instance.
*/
find (expr) {
return collectionFind(this.getSession(), this.getSchema(), this.getName(), expr);
},
/**
* Set alias for link operation
* @param {String} alias
* @returns {Collection}
*/
Collection.prototype.as = function (alias) {
return new Collection(this._session, this._schema, this._collection, alias, this._links);
};
/**
* Retrieve the collection name.
* @function
* @name module:Collection#getName
* @returns {string}
*/
getName () {
return state.name;
},
Collection.prototype.inspect = function () {
return { schema: this._schema.getName(), collection: this._collection };
};
/**
* Retrieve a single document with the given id.
* @function
* @name module:Collection#getOne
* @param {string} id - document id
* @example
* collection.getOne('1')
* @returns {Object} The document instance.
*/
getOne (id) {
let instance;
return this
.find(`$._id == "${escapeQuotes(id)}"`)
.execute(doc => {
instance = doc || null;
})
.then(() => instance);
},
/**
* Retrieve the schema associated to the collection.
* @function
* @name module:Collection#getSchema
* @returns {Schema}
*/
getSchema () {
return state.schema;
},
/**
* Retrieve the collection metadata.
* @function
* @name module:Collection#inspect
* @returns {Object} An object containing the relevant metadata.
*/
inspect () {
return { schema: this.getSchema().getName(), collection: this.getName() };
},
/**
* Create an operation to modify documents in the collection.
* @function
* @name module:Collection#modify
* @param {SearchConditionStr} expr - filtering criteria
* @example
* // update all documents in a collection
* collection.modify('true').set('name', 'bar')
*
* // update documents that match a given condition
* collection.modify('$.name == "foo"').set('name', 'bar')
* @returns {CollectionModify} The operation instance.
*/
modify (expr) {
return collectionModify(this.getSession(), this.getSchema(), this.getName(), expr);
},
/**
* Create an operation to remove documents from the collection.
* @function
* @name module:Collection#remove
* @param {SearchConditionStr} expr - filtering criteria
* @example
* // remove all documents from a collection
* collection.remove('true')
*
* // remove documents that match a given condition
* collection.remove('$.name == "foobar"')
* @returns {CollectionRemove} The operation instance.
*/
remove (expr) {
return collectionRemove(this.getSession(), this.getSchema(), this.getName(), expr);
},
/**
* Remove a single document with the given id.
* @function
* @name module:Collection#removeOne
* @param {string} id - document id
* @example
* collection.removeOne('1')
* @returns {Promise.<Result>} A promise that resolves to the operation result.
*/
removeOne (id) {
return this.remove(`$._id == "${escapeQuotes(id)}"`).execute();
},
/**
* Replace an entire document with a given id.
* @function
* @name module:Collection#replaceOne
* @param {string} id - document id
* @param {Object} data - document properties
* @example
* collection.replaceOne('foo', { prop1: 'bar', prop2: 'baz' })
* @returns {Promise.<Result>} A promise that resolves to the operation result.
*/
replaceOne (id, data) {
const docs = [];
// TODO(Rui): all this can be boiled down to the following, as soon as the expression parser is replaced
// return this.modify(`$._id == "${escapeQuotes(id)}"`).set('$', data).execute()
return this
.find(`$._id == "${escapeQuotes(id)}"`)
.execute(doc => doc && docs.push(doc))
.then(() => {
const query = this.modify(`$._id == "${escapeQuotes(id)}"`);
if (docs.length) {
const toReplace = Object.keys(docs[0])
.filter(key => key !== `_id`)
.map(key => `$.${key}`);
query.unset(toReplace);
}
Object.keys(data).forEach(key => {
if (key === `_id`) {
return;
}
query.set(`$.${key}`, data[key]);
});
return query.execute();
});
}
});
}
module.exports = Collection;
/*
* Copyright (c) 2015, 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -34,67 +34,90 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

/**
* Operation to add documents to a collection.
* @param {Session} session
* @param {Schema} schema
* @param {Collection} collection
* @param {object} document
* @constructor
* CollectionAdd factory.
* @module CollectionAdd
*/
function CollectionAdd (session, schema, collection, document) {
this._session = session;
this._schema = schema;
this._collection = collection;
this._document = document || [];
}
module.exports = CollectionAdd;
/**
* Create query to add one or various documents.
* @param {...Object|Object[]} document - object with document data
* @throws {Error} When the input type is invalid.
* @example
* // arguments as single documents
* collection.add({ foo: 'baz' }).add({ bar: 'qux' }, { biz: 'quux' })
*
* // array of documents
* collection.add([{ foo: 'baz' }]).add([{ bar: 'qux' }, { biz: 'quux' }])
* @private
* @alias module:CollectionAdd
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} collection - collection name
* @param {Object} documents - documents to add
* @returns {CollectionAdd}
*/
CollectionAdd.prototype.add = function () {
const documents = parseFlexibleParamList(Array.prototype.slice.call(arguments));
function CollectionAdd (session, schema, collection, documents, options) {
let state = { session, schema, collection, documents: documents || [], options: Object.assign({}, { upsert: false }, options) };
this._document = this._document.concat(documents);
return {
/**
* Run the query to save the documents to the collection in the database.
* If a document does not contain an <code>_id</code>, it will be assigned a UUID-like value.
* @function
* @name module:CollectionAdd#execute
* @tutorial Working_with_Documents
* @returns {Promise.<Result>}
*/
execute () {
if (!state.documents.length) {
return Promise.resolve();
}
return this;
};
const docs = state.documents.map(doc => {
if (typeof doc._id !== 'undefined') {
return Object.assign({}, doc);
}
/**
* Run the query to save the documents to the collection in the database.
* If a document does not contain an <code>_id</code>, it will be assigned a UUID-like value.
* @tutorial Working_with_Documents
* @returns {Promise.<Result>}
*/
CollectionAdd.prototype.execute = function () {
if (!this._document.length) {
return Promise.resolve();
}
return Object.assign({}, doc, { _id: state.session.idGenerator() });
});
const documentIds = [];
return state
.session
._client
.crudInsert(state.schema.getName(), state.collection, Client.dataModel.DOCUMENT, { rows: docs.map(doc => [JSON.stringify(doc)]) }, state.options)
.then(state => new Result(Object.assign({}, state, { doc_ids: docs.map(doc => doc._id) })));
},
const documents = this._document.map(doc => {
if (typeof doc._id === 'undefined') {
doc._id = this._session.idGenerator();
}
/**
* Create query to add one or various documents.
* @function
* @name module:CollectionAdd#add
* @param {...Object|Object[]} input - document or list of documents
* @throws {Error} When the input type is invalid.
* @example
* // arguments as single documents
* collection.add({ foo: 'baz' }).add({ bar: 'qux' }, { biz: 'quux' })
*
* // array of documents
* collection.add([{ foo: 'baz' }]).add([{ bar: 'qux' }, { biz: 'quux' }])
* @returns {CollectionAdd} The operation instance.
*/
add () {
const documents = parseFlexibleParamList(Array.prototype.slice.call(arguments));
documentIds.push(doc._id);
state.documents = state.documents.concat(documents);
return [ JSON.stringify(doc) ];
});
return this;
},
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:CollectionAdd#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'CollectionAdd';
},
return this._session._client
.crudInsert(this._schema.getName(), this._collection, Client.dataModel.DOCUMENT, documents)
.then(state => {
state.doc_ids = documentIds;
return new Result(state);
});
};
/**
* Retrieve the documents scheduled to be added.
* @function
* @name module:CollectionAdd#getDocuments
* @returns {Object} The set of documents.
*/
getDocuments () {
return state.documents;
}
};
}
module.exports = CollectionAdd;
/*
* Copyright (c) 2015, 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -29,101 +29,97 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Result = require('./Result');
const util = require('util');
const binding = require('./Binding');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
const locking = require('./Locking');
/**
* A callback for a single document
*
* This will be called for each document received from a Collection
*
* @callback documentCallback The document as stored in the database
* @param {object} object
* CollectionFind factory.
* @module CollectionFind
* @mixes Filtering
* @mixes Binding
* @mixes Limiting
* @mixes Locking
*/
/**
*
* @param {Session} session
* @param {Schema} schema
* @param {Collection} collection
* @param {String} [query]
* @constructor
* @augments BaseQuery
* @private
* @alias module:CollectionFind
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} collection - collection name
* @param {string} [criteria] - filtering criteria expression
* @returns {CollectionFind}
*/
function CollectionFind (session, schema, collection, query) {
BaseQuery.call(this);
function CollectionFind (session, schema, collection, criteria) {
let state = Object.assign({ projection: [] }, { session, schema, collection });
this._session = session;
this._schema = schema;
this._collection = collection;
this._query = query;
this._bounds = {};
this._projection = [];
}
return Object.assign({}, filtering({ criteria }), binding(), limiting(), locking(), {
/**
* Document property projection expressions.
* @typedef {ProjectedSearchExprStrList|Expression} ProjectedDocumentExprStr
*/
util.inherits(CollectionFind, BaseQuery);
/**
* Pick collection properties for projection.
* @function
* @name module:CollectionFind#fields
* @param {ProjectedDocumentExprStr} projection - expression with the properties to pick
* @returns {CollectionFind} The operation instance.
*/
fields (projection) {
if (!Array.isArray(projection)) {
throw new Error('Argument to fields() must be an array of field selectors');
}
module.exports = CollectionFind;
state.projection = projection;
CollectionFind.prototype.fields = function (fields) {
if (!Array.isArray(fields)) {
throw new Error('Argument to fields() must be an array of field selectors');
}
return this;
},
this._projection = fields;
/**
* Cursor callback.
* @callback documentCallback
* @global
* @param {object} object - the document in the cursor position
*/
return this;
};
/**
* Execute find operation.
* @function
* @name module:CollectionFind#execute
* @param {documentCallback} [rowcb]
* @return {Promise.<Result>}
*/
execute (rowcb) {
let cb;
/**
* Bind values to query parameters.
* @param {string|Object} parameter - parameter name or mapping object
* @param {string} [value]
* @example
* // parameter name and value as arguments
* const query = collection.find('$.foo == :foo').bind('foo', 'bar')
*
* // parameter name and value as key-value pair in an object
* const query = collection.find('$.foo == :foo').bind({ foo: 'bar' })
* @returns {TableSelect}
*/
CollectionFind.prototype.bind = function () {
if (!arguments.length) {
return this;
}
if (rowcb) {
cb = function (row) {
rowcb(row[0]);
};
}
let bound;
const joins = [];
if (Object(arguments[0]) === arguments[0]) {
bound = arguments[0];
} else {
bound = { [arguments[0]]: arguments[1] };
}
return state
.session
._client
.crudFind(state.session, state.schema.getName(), state.collection, Client.dataModel.DOCUMENT, state.projection, this.getCriteria(), null, null, null, this.getLimit(), cb, null, this.getBindings(), joins, this.getLockingMode())
.then(state => new Result(state));
},
this._bounds = Object.assign(this._bounds, bound);
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:CollectionFind#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'CollectionFind';
}
});
}
return this;
};
/**
* Execute find operation
* @param {documentCallback} [rowcb]
* @return {Promise.<Result>}
*/
CollectionFind.prototype.execute = function (rowcb) {
let cb;
if (rowcb) {
cb = function (row) {
rowcb(row[0]);
};
}
const joins = [];
return this
._session
._client
.crudFind(this._session, this._schema.getName(), this._collection, Client.dataModel.DOCUMENT, this._projection, this._query, null, null, null, this._limit, cb, null, this._bounds, joins)
.then(state => new Result(state));
};
module.exports = CollectionFind;

@@ -29,119 +29,159 @@ /*

const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Expressions = require('../Expressions');
const Result = require('./Result');
const util = require('util');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
/**
*
* @param {Session} session
* @param {Schema} schema
* @param {Collection} collection
* @param {String} [query]
* @constructor
* CollectionModify factory.
* @module CollectionModify
* @mixes Filtering
* @mixes Limiting
*/
function CollectionModify (session, schema, collection, query) {
BaseQuery.call(this);
this._session = session;
this._schema = schema;
this._collection = collection;
this._query = query;
this._operations = [];
}
/**
* @private
* @alias module:CollectionModify
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} collection - collection name
* @param {string} [criteria] - filtering criteria expression
* @returns {CollectionModify}
*/
function CollectionModify (session, schema, collection, criteria) {
let state = Object.assign({ operations: [] }, { session, schema, collection });
util.inherits(CollectionModify, BaseQuery);
return Object.assign({}, limiting(), filtering({ criteria }), {
/**
* Append element to an array field.
* @function
* @name module:CollectionModify#arrayAppend
* @param {string} field - document array field
* @param {} value - value to append
* @returns {CollectionModify} The operation instance.
*/
arrayAppend (field, value) {
state.operations.push(getOperation(Client.updateOperations.ARRAY_APPEND, field, value));
module.exports = CollectionModify;
return this;
},
function getOperation(op, field, expression) {
var fieldExpression = Expressions.parse(field);
if (!fieldExpression.expr.identifier) {
throw new Error("Field expression has to be identifier while parsing " + field);
}
var operation = {
source: fieldExpression.expr.identifier,
operation: op
};
/**
* Delete element from an array.
* @function
* @name module:CollectionModify#arrayDelete
* @param {string} field - document array field
* @param {} value - value to delete
* @returns {CollectionModify} The operation instance.
*/
arrayDelete (field, expression) {
state.operations.push(getOperation(Client.updateOperations.ITEM_REMOVE, field, expression));
if (expression) {
operation.value = Expressions.literalOrParsedExpression(expression);
}
return this;
},
return operation;
}
/**
* Insert element into an array field.
* @function
* @name module:CollectionModify#arrayInsert
* @param {string} field - document array field
* @param {} value - value to insert
* @returns {CollectionModify} The operation instance.
*/
arrayInsert (field, value) {
state.operations.push(getOperation(Client.updateOperations.ARRAY_INSERT, field, value));
/**
* Set a field in the document
* @param {String} field Field descriptor
* @param {String} expression Replacement expression
* @returns {CollectionModify}
*/
CollectionModify.prototype.set = function (field, expression) {
this._operations.push(getOperation(Client.updateOperations.ITEM_SET, field, expression));
return this;
};
return this;
},
/**
* Unset fields from document
*
* @param {Array.<String>|String} fields
* @returns {CollectionModify}
*/
CollectionModify.prototype.unset = function (fields) {
if (typeof fields === "string") {
fields = [ fields ];
}
fields = fields.map(field => getOperation(Client.updateOperations.ITEM_REMOVE, field));
this._operations = this._operations.concat(fields);
/**
* Execute modify operation.
* @function
* @name module:CollectionModify#execute
* @return {Promise.<Result>}
*/
execute () {
if (typeof this.getCriteria() !== 'string' || this.getCriteria().trim().length < 1) {
return Promise.reject(new Error('remove needs a valid condition'));
}
return this;
};
return state
.session
._client
.crudModify(state.schema.getName(), state.collection, Client.dataModel.DOCUMENT, this.getCriteria(), state.operations, this.getLimit(), {})
.then(state => new Result(state));
},
CollectionModify.prototype.merge = function () {
throw new Error("modify.merge currently not implemented");
};
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:CollectionModify#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'CollectionModify';
},
/**
*
* @param field
* @param expression
* @returns {CollectionModify}
*/
CollectionModify.prototype.arrayInsert = function (field, expression) {
this._operations.push(getOperation(Client.updateOperations.ARRAY_INSERT, field, expression));
return this;
};
merge () {
throw new Error('modify.merge currently not implemented');
},
/**
* Append an element to an array
* @param {String} field
* @param {String} expression
* @returns {CollectionModify}
*/
CollectionModify.prototype.arrayAppend = function (field, expression) {
this._operations.push(getOperation(Client.updateOperations.ARRAY_APPEND, field, expression));
return this;
};
/**
* Set the value of a given document field.
* @function
* @name module:CollectionModify#set
* @param {string} field - document field
* @param {*} value - value to assign
* @returns {CollectionModify} The operation instance.
*/
set (field, value) {
state.operations.push(getOperation(Client.updateOperations.ITEM_SET, field, value));
CollectionModify.prototype.arrayDelete = function (field, expression) {
this._operations.push(getOperation(Client.updateOperations.ITEM_REMOVE, field, expression));
return this;
};
return this;
},
/**
* Unset the value of document fields.
* @function
* @name module:CollectionModify#unset
* @param {Array.<String>|String} fields
* @returns {CollectionModify} The operation instance.
*/
unset (fields) {
if (typeof fields === 'string') {
fields = [ fields ];
}
fields = fields.map(field => getOperation(Client.updateOperations.ITEM_REMOVE, field));
state.operations = state.operations.concat(fields);
return this;
}
});
}
/**
* Execute find operation
* @return {Promise.<Result>}
* @private
*/
CollectionModify.prototype.execute = function () {
if (typeof this._query !== 'string' || this._query.trim().length < 1) {
return Promise.reject(new Error('remove needs a valid condition'));
function getOperation (op, field, expression) {
const fieldExpression = Expressions.parse(field);
if (!fieldExpression.expr.identifier) {
throw new Error(`Field expression has to be identifier while parsing ${field}`);
}
return this
._session
._client
.crudModify(this._schema.getName(), this._collection, Client.dataModel.DOCUMENT, this._query, this._operations, this._limit, {})
.then(state => new Result(state));
};
const operation = {
source: fieldExpression.expr.identifier,
operation: op
};
if (expression) {
operation.value = Expressions.literalOrParsedExpression(expression);
}
return operation;
}
module.exports = CollectionModify;

@@ -29,49 +29,59 @@ /*

// TODO: Lots of duplication with CollectionFind ....
const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Result = require('./Result');
const util = require('util');
const binding = require('./Binding');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
/**
*
* @param {Session} session
* @param {Schema} schema
* @param {Collection} collection
* @param {String} [query]
* @constructor
* CollectionRemove factory.
* @module CollectionRemove
* @mixes Filtering
* @mixes Binding
* @mixes Limiting
*/
function CollectionRemove (session, schema, collection, query) {
BaseQuery.call(this);
this._session = session;
this._schema = schema;
this._collection = collection;
this._query = query;
this._bounds = {};
}
/**
* @private
* @alias module:CollectionRemove
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} collection - collection name
* @param {string} [criteria] - filtering criteria expression
* @returns {CollectionRemove}
*/
function CollectionRemove (session, schema, collection, criteria) {
let state = Object.assign({}, { session, schema, collection });
util.inherits(CollectionRemove, BaseQuery);
return Object.assign({}, filtering({ criteria }), binding(), limiting(), {
/**
* Execute remove operation.
* @function
* @name module:CollectionRemove#execute
* @return {Promise.<Result>}
*/
execute () {
if (typeof this.getCriteria() !== 'string' || this.getCriteria().trim().length < 1) {
return Promise.reject(new Error('remove needs a valid condition'));
}
module.exports = CollectionRemove;
return state
.session
._client
.crudRemove(state.schema.getName(), state.collection, Client.dataModel.DOCUMENT, this.getCriteria(), this.getLimit(), this.getBindings())
.then(state => new Result(state));
},
CollectionRemove.prototype.bind = function (bind) {
this._bounds = Object.assign({}, this._bounds, bind);
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:CollectionRemove#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'CollectionRemove';
}
});
}
return this;
};
/**
* Remove documents from a collection.
*/
CollectionRemove.prototype.execute = function () {
if (typeof this._query !== 'string' || this._query.trim().length < 1) {
return Promise.reject(new Error('remove needs a valid condition'));
}
return this
._session
._client
.crudRemove(this._schema.getName(), this._collection, Client.dataModel.DOCUMENT, this._query, this._limit, this._bounds).then(state => new Result(state));
};
module.exports = CollectionRemove;
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -33,6 +33,6 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

* Column Definition for table creation. This is typically created via {@link Schema#columnDef}
* and injected into {@link TableCreater#addColumn}.
* and injected into {@link TableFactory#addColumn}.
*
* @see {Schema#columnDef}
* @see {TableCreater#addColumn}
* @see {TableFactory#addColumn}
* @param {string} name

@@ -39,0 +39,0 @@ * @param type

@@ -30,28 +30,24 @@ /*

/**
* Base object for different dataase objects
* @param {Session} session - session instance
* @param {string} schema - schema name
* @constructor
* DatabaseObject mixin.
* @mixin
* @alias DatabaseObject
* @param {Session} session
* @returns {DatabaseObject}
*/
function DatabaseObject (session, schema) {
this._session = session;
this._schema = schema;
function DatabaseObject (session) {
const state = Object.assign({}, { session });
/**
* Retrieve the session associated to this entity.
* @function
* @name DatabaseObject#getSession
* @returns {Session} The session the entity is bound to.
*/
return {
getSession () {
return state.session;
}
};
}
module.exports = DatabaseObject;
/**
* Get session related to this object
* @returns {Session|*}
*/
DatabaseObject.prototype.getSession = function () {
return this._session;
};
/**
* Get the current schema name.
* @returns {string}
*/
DatabaseObject.prototype.getSchema = function () {
return this._schema;
};

@@ -51,12 +51,2 @@ /*

cache: {},
config: {
system: {
unix: path.resolve('/', 'etc', 'mysql', filename),
windows: path.resolve(os.homedir(), 'PROGRAMDATA', 'MySQL', filename)
},
user: {
unix: path.resolve(os.homedir(), '.mysql', filename),
windows: path.resolve(os.homedir(), 'APPDATA', 'MySQL', filename)
}
},
platform: os.platform() !== 'win32' ? 'unix' : 'windows'

@@ -84,3 +74,5 @@ }, state);

function getSystemConfigPath () {
return process.env.MYSQL_SYSTEM_CONFIG || state.config.system[state.platform];
return process.env.MYSQL_SYSTEM_CONFIG || (state.platform === 'unix'
? path.resolve('/', 'etc', 'mysql', filename)
: path.join(process.env.PROGRAMDATA, 'MySQL', filename));
}

@@ -108,3 +100,5 @@

function getUserConfigPath () {
return process.env.MYSQL_USER_CONFIG || state.config.user[state.platform];
return process.env.MYSQL_USER_CONFIG || (state.platform === 'unix'
? path.resolve(os.homedir(), '.mysql', filename)
: path.join(process.env.APPDATA, 'MySQL', filename));
}

@@ -111,0 +105,0 @@

/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -40,6 +40,6 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

* Object describing a Foreign Key Definition. Created usually via {@link Schema#foreignKey} and
* injected to {@link TableCreater#addForeignKey}.
* injected to {@link TableFactory#addForeignKey}.
*
* @see {Schema#foreignKey}
* @see {TableCreater#addForeignKey}
* @see {TableFactory#addForeignKey}
* @param {Schema} schema

@@ -126,2 +126,2 @@ * @constructor

;
};
};

@@ -29,318 +29,366 @@ /*

const Collection = require('./Collection');
const ColumnDefinition = require('./ColumnDefinition');
const DatabaseObject = require('./DatabaseObject');
const ForeignKeyDefinition = require('./ForeignKeyDefinition');
const Table = require('./Table');
const TableCreator = require('./TableCreater');
const ViewCreator = require('./ViewCreate');
const util = require('util');
const TableFactory = require('./TableFactory');
const ViewFactory = require('./ViewFactory');
const collection = require('./Collection');
const databaseObject = require('./DatabaseObject');
const table = require('./Table');
/**
* Access to Schema-related operations
*
* Usually you shouldn't create this yourself but via session object
*
* @param {Session} session
* @param {string} schema Name of the schema
* @constructor
* @extends DatabaseObject
* Schema factory.
* @module Schema
* @mixes DatabaseObject
*/
function Schema (session, schema) {
DatabaseObject.call(this, session, schema);
}
module.exports = Schema;
util.inherits(Schema, DatabaseObject);
/**
* Get the name of this schema
* @returns {string}
* @private
* @alias module:Schema
* @param {Session} session - session to bind
* @param {string} name - schema name
* @returns {Schema}
*/
Schema.prototype.getName = function () {
return this._schema;
};
function Schema (session, name) {
const state = Object.assign({}, { name, session });
/**
* Verifies this schema exists
*
* @returns {Promise<boolean>}
*/
Schema.prototype.existsInDatabase = function () {
let status = false;
return Object.assign({}, databaseObject(session), {
/**
* Type flags for table creation
* @type {{Bit: string, Tinyint: string, Smallint: string, Mediumint: string, Int: string, Bigint: string, Real: string, Double: string, Float: string, Decimal: string, Numeric: string, Date: string, Time: string, Timestamp: string, Datetime: string, Year: string, Char: string, Varchar: string, Binary: string, Varbinary: string, Tinyblob: string, Blob: string, Mediumblob: string, Longblob: string, Tinytext: string, Text: string, Mediumtext: string, Enum: string, Set: string, Json: string}}
*/
Type: {
Bit: 'BIT',
Tinyint: 'TINYINT',
Smallint: 'SMALLINT',
Mediumint: 'MEDIUMINT',
Int: 'INT',
Bigint: 'BIGINT',
Real: 'REAL',
Double: 'DOUBLE',
Float: 'FLOAT',
Decimal: 'DECIMAL',
Numeric: 'NUMERIC',
Date: 'DATE',
Time: 'TIME',
Timestamp: 'TIMESTAMP',
Datetime: 'DATETIME',
Year: 'YEAR',
Char: 'CHAR',
Varchar: 'VARCHAR',
Binary: 'BINARY',
Varbinary: 'VARBINARY',
Tinyblob: 'TINYBLOB',
Blob: 'BLOB',
Mediumblob: 'MEDIUMBLOB',
Longblob: 'LONGBLOB',
Tinytext: 'TINYTEXT',
Text: 'TEXT',
Mediumtext: 'MEDIUMTEXT',
Enum: 'ENUM',
Set: 'SET',
Json: 'JSON'
},
return this._session._client
.sqlStmtExecute('SHOW DATABASES LIKE ?', [this._schema], (found) => { status = !!found.length; })
.then(() => status);
};
/**
* Create an operation to alter a view.
* @function
* @name module:Schema#alterView
* @param {string} name
* @returns {ViewFactory}
*/
alterView (name) {
return new ViewFactory(this, name, true);
},
/**
* List objects of a given type using a factory function.
*/
function _listObjects (type, factory) {
const result = {};
const callback = (row) => {
if (row[1] === type || type === 'TABLE' && row[1] === 'VIEW') {
result[row[0]] = factory.call(this, row[0]);
}
};
/**
* Create a Column Definition object which can be injected into {@link TableCreater#addColumn}.
* @function
* @name module:Schema#columnDef
* @see {TableFactory#addColumn}
* @param {string} name - field name
* @param {string} type - field type, usually a member of {@link Schema#Type} should be used
* @param {number} length
* @returns {ColumnDefinition}
*/
columnDef (name, type, length) {
return new ColumnDefinition(name, type, length);
},
return this._session._client
.sqlStmtExecute('list_objects', [this._schema], callback, null, 'xplugin')
.then(() => result);
};
/**
* Options available for creating a new collection.
* @typedef {object} CreateCollectionOptions
* @property {bool} [ReuseExistingObject] re-use or throw error if a collection with the same name already exists
*/
/**
* Get collections
*
* @returns {Promise.<CollectionList>} Promise resolving to an object of Collection name <-> Collection object pairs
*/
Schema.prototype.getCollections = function () {
return _listObjects.call(this, 'COLLECTION', this.getCollection);
};
/**
* Create a new collection in the schema.
* @function
* @name module:Schema#createCollection
* @param {string} collection - collection name
* @param {CreateCollectionOptions} [options] - setup options
* @returns {Promise.<Collection>}
*/
createCollection (collection, options) {
options = Object.assign({}, { ReuseExistingObject: false }, options);
/**
* Get a Collection object
*
* This will always succeed
*
* @param {string} collection Name of the collection
* @returns {Collection}
*/
Schema.prototype.getCollection = function (collection) {
return new Collection(this._session, this, collection);
};
const args = [this.getName(), collection];
/**
* Options
* @typedef {object} createOptions
* @property {bool} [ReuseExistingObject] If true this won't error if the collection exists already
*/
return this
.getSession()
._client
.sqlStmtExecute('create_collection', args, null, null, 'xplugin')
.then(() => this.getCollection(collection))
.catch(err => {
if (err.info.code !== 1050 || !options.ReuseExistingObject) {
throw err;
}
/**
* Create a new collection
*
* The returned Promise object will resolve on success or provide an Error
*
* @param {string} collection Name of the collection
* @param {CreateOptions} [options]
* @returns {Promise.<Collection>}
*/
Schema.prototype.createCollection = function (collection, options) {
options = Object.assign({}, { ReuseExistingObject: false }, options);
return this.getCollection(collection);
});
},
const args = [this._schema, collection];
/**
* Retrieve the factory to create a new table in the schema (examples available in the {@tutorial Table_Creation_API} tutorial).
* @function
* @name module:Schema#createTable
* @param {string} name - table name
* @param {bool} replace - re-use or throw error if a table with the same name already exists
* @return {TableFactory}
*/
createTable (name, replace) {
return new TableFactory(this, name, replace);
},
return this._session._client.sqlStmtExecute('create_collection', args, null, null, 'xplugin')
.then(() => this.getCollection(collection))
.catch(err => {
if (err.info.code !== 1050 || !options.ReuseExistingObject) {
throw err;
}
/**
* Retrieve the factory to create a new view in the schema.
* @function
* @name module:Schema#createView
* @param {string} name
* @param {bool} replace - re-use or throw error if a table with the same name already exists
* @returns {ViewFactory}
*/
createView (name, replace) {
return new ViewFactory(this, name, replace);
},
return this.getCollection(collection);
});
};
/**
* Drop a collection from the schema (without failing even if the collection does not exist).
* @function
* @name module:Schema#dropCollection
* @param {string} name - collection name
* @returns {Promise.<boolean>}
*/
dropCollection (name) {
return this
.getSession()
._client
.sqlStmtExecute('drop_collection', [this.getName(), name], null, null, 'xplugin')
.then(() => true)
.catch(err => {
// Don't fail if the collection does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Create a new Table in this schema.
*
*
* An example for using this function can be found in the {@tutorial Table_Creation_API} tutorial.
*
*
* @param {String} name Name of the new table
* @param {bool} reuseExisting Flag whether to reuse an existing table, defaults to false
* @return {TableCreater}
*/
Schema.prototype.createTable = function (name, reuseExisting) {
return new TableCreator(this, name, reuseExisting);
};
return true;
});
},
/**
* Type flags for table creation
* @type {{Bit: string, Tinyint: string, Smallint: string, Mediumint: string, Int: string, Bigint: string, Real: string, Double: string, Float: string, Decimal: string, Numeric: string, Date: string, Time: string, Timestamp: string, Datetime: string, Year: string, Char: string, Varchar: string, Binary: string, Varbinary: string, Tinyblob: string, Blob: string, Mediumblob: string, Longblob: string, Tinytext: string, Text: string, Mediumtext: string, Enum: string, Set: string, Json: string}}
*/
Schema.prototype.Type = {
Bit: 'BIT',
Tinyint: 'TINYINT',
Smallint: 'SMALLINT',
Mediumint: 'MEDIUMINT',
Int: 'INT',
Bigint: 'BIGINT',
Real: 'REAL',
Double: 'DOUBLE',
Float: 'FLOAT',
Decimal: 'DECIMAL',
Numeric: 'NUMERIC',
Date: 'DATE',
Time: 'TIME',
Timestamp: 'TIMESTAMP',
Datetime: 'DATETIME',
Year: 'YEAR',
Char: 'CHAR',
Varchar: 'VARCHAR',
Binary: 'BINARY',
Varbinary: 'VARBINARY',
Tinyblob: 'TINYBLOB',
Blob: 'BLOB',
Mediumblob: 'MEDIUMBLOB',
Longblob: 'LONGBLOB',
Tinytext: 'TINYTEXT',
Text: 'TEXT',
Mediumtext: 'MEDIUMTEXT',
Enum: 'ENUM',
Set: 'SET',
Json: 'JSON'
};
/**
* Drop a table from the schema (without failing even if the table does not exist).
* @function
* @name module:Schema#dropTable
* @param {string} name - table name
* @returns {Promise.<boolean>}
*/
// TODO(Rui): remove duplication with dropCollection.
dropTable (name) {
const schema = table.escapeIdentifier(this.getName());
const tableName = table.escapeIdentifier(name);
/**
* Create a Column Definition object which can be injected into
* {@link TableCreater#addColumn}
*
* @see {TableCreater#addColumn}
* @param {string} name - Name for the field
* @param {String} type - The type for the field, usually a member of {@link Schema#Type} should be used
* @param {Number=} length
* @returns {ColumnDefinition]
*/
Schema.prototype.columnDef = function (name, type, length) {
return new ColumnDefinition(name, type, length);
};
return this
.getSession()
._client
.sqlStmtExecute(`DROP TABLE ${schema}.${tableName}`)
.then(() => true)
.catch(err => {
// Don't fail if the table does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Foreign Key Definition for Table Creation
*
* @returns {ForeignKeyDefinition}
*/
Schema.prototype.foreignKey = function () {
return new ForeignKeyDefinition(this);
};
return true;
});
},
/**
* Create a new view
*
* This will return an object which can be used to create a view
*
* @param {string} name
* @param {bool} replace
* @returns {ViewCreate}
*/
Schema.prototype.createView = function (name, replace) {
return new ViewCreator(this, name, replace);
};
/**
* Drop a view from the schema (without failing even if the view does not exist).
* @function
* @name module:Schema#dropView
* @param {String} name - view name
* @returns {Promise.<boolean>}
*/
// TODO(Rui): remove duplication with dropCollection.
dropView (name) {
const schema = table.escapeIdentifier(this.getName());
const view = table.escapeIdentifier(name);
/**
* Alters a view
*
* This will return an object which can be used to create a new view definition
*
* @param {string} name
* @param {bool} replace
* @returns {ViewCreate}
*/
Schema.prototype.alterView = function (name) {
return new ViewCreator(this, name, true);
};
return this
.getSession()
._client
.sqlStmtExecute(`DROP VIEW ${schema}.${view}`)
.then(() => true)
.catch(err => {
// Don't fail if the view does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Drop a view (without failing even if the view does not exist).
* @param {String} name - view name
* @returns {Promise.<boolean>}
*/
Schema.prototype.dropView = function (name) {
const schema = Table.escapeIdentifier(this._schema);
const view = Table.escapeIdentifier(name);
return true;
});
},
return this._session._client
.sqlStmtExecute(`DROP VIEW ${schema}.${view}`)
.then(() => true)
.catch(err => {
// Don't fail if the view does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Check if this schema exists in the database.
* @function
* @name module:Schema#existsInDatabase
* @returns {Promise.<boolean>}
*/
// TODO(Rui): extract method into a proper aspect (to be used on Collection, Schema and Table).
existsInDatabase () {
let status = false;
return true;
});
};
return this
.getSession()
._client
.sqlStmtExecute('SHOW DATABASES LIKE ?', [this.getName()], (found) => { status = !!found.length; })
.then(() => status);
},
/**
* Drop a collection (without failing even if the collection does not exist).
* @param {string} name - collection name
* @returns {Promise.<boolean>}
*/
Schema.prototype.dropCollection = function (name) {
return this._session._client
.sqlStmtExecute('drop_collection', [this._schema, name], null, null, 'xplugin')
.then(() => true)
.catch(err => {
// Don't fail if the collection does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Create a Foreign Key Definition for table creation.
* @function
* @name module:Schema:foreignKey
* @returns {ForeignKeyDefinition}
*/
foreignKey () {
return new ForeignKeyDefinition(this);
},
return true;
});
};
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:Schema:getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'Schema';
},
/**
* Get tables
*
* @returns {Promise.<TableList>} Promise resolving to an object of Table name <-> Table object pairs
*/
Schema.prototype.getTables = function () {
return _listObjects.call(this, 'TABLE', this.getTable);
};
/**
* Retrieve the instance of a given collection.
* @function
* @name module:Schema:getCollection
* @param {string} name - collection name
* @returns {Collection}
*/
getCollection (name) {
return collection(this.getSession(), this, name);
},
/**
* Get a Table object
*
* This will always succeed
*
* @param {string} table Name of the table
* @returns {Table}
*/
Schema.prototype.getTable = function (table) {
return new Table(this._session, this, table);
};
/**
* Retrieve the instance of a given table or named collection.
* @function
* @name module:Schema:getCollectionAsTable
* @param {string} name - collection name
* @returns {Table}
*/
getCollectionAsTable (name) {
return this.getTable(name);
},
/**
* Get a Table object or a given named Collection
*
* @param {string} collection Name of the collection
* @returns {Table}
*/
Schema.prototype.getCollectionAsTable = function (collection) {
return new Table(this._session, this, collection);
};
/**
* List of available collections.
* @typedef {object} CollectionList
* @property {Collection} [<collection name>] The collection instance
*/
/**
* Drop a collection (without failing even if the table does not exist).
* @param {string} name - table name
* @returns {Promise.<boolean>}
*/
Schema.prototype.dropTable = function (name) {
const schema = Table.escapeIdentifier(this._schema);
const table = Table.escapeIdentifier(name);
/**
* Retrieve the list of collections that exist in the schema.
* @function
* @name module:Schema:getCollections
* @returns {Promise.<CollectionList>}
*/
getCollections () {
return _listObjects.call(this, 'COLLECTION', this.getCollection);
},
return this._session._client
.sqlStmtExecute(`DROP TABLE ${schema}.${table}`)
.then(() => true)
.catch(err => {
// Don't fail if the table does not exist.
if (!err.info || err.info.code !== 1051) {
throw err;
}
/**
* Retrieve the schema name.
* @function
* @name module:Schema:getName
* @returns {string}
*/
getName () {
return state.name;
},
return true;
});
};
/**
* Retrieve the instance of a given table.
* @function
* @name module:Schema:getTable
* @param {string} name - table name
* @returns {Table}
*/
getTable (name) {
return table(this.getSession(), this, name);
},
/**
* List of available tables.
* @typedef {object} TableList
* @property {Table} [<table name>] The table instance
*/
/**
* Retrieve the list of tables that exist in the schema.
* @function
* @name module:Schema:getTables
* @returns {Promise.<TableList>}
*/
getTables () {
return _listObjects.call(this, 'TABLE', this.getTable);
},
/**
* Retrieve the schema metadata.
* @function
* @name module:Schema#inspect
* @returns {Object} An object containing the relevant metadata.
*/
inspect () {
return { schema: this.getName() };
}
});
}
/**
* Inspect schema
* List objects of a given type using a factory function.
* @private
*/
Schema.prototype.inspect = function () {
return { schema: this._schema };
function _listObjects (type, factory) {
const result = {};
const callback = (row) => {
if (row[1] === type || type === 'TABLE' && row[1] === 'VIEW') {
result[row[0]] = factory.call(this, row[0]);
}
};
return this
.getSession()
._client
.sqlStmtExecute('list_objects', [this.getName()], callback, null, 'xplugin')
.then(() => result);
};
module.exports = Schema;

@@ -35,3 +35,2 @@ /*

const PlainAuth = require('../Authentication/PlainAuth');
const Schema = require('./Schema.js');
const SocketFactory = require('../SocketFactory');

@@ -42,2 +41,3 @@ const Statement = require('./Statement');

const parseUri = require('./Util/URIParser');
const schema = require('./Schema.js');
const uuid = require('./Util/UUID');

@@ -76,5 +76,5 @@

* @property {string} dbPassword Password
* @property {string} authMethod Name of an authentication mehod to use (default: MySQL41)
* @property {string} auth Name of an authentication mehod to use (default: PLAIN)
* @property {SocketFactory} socketFactory A factory which can creaes socket, usually not needed outside tests
* @property {bool} ssl Enable SSL, defaults to false
* @property {bool} ssl Enable SSL, defaults to true
* @property {object} sslOption options passed to tls.TLSSocket constructor, see https://nodejs.org/api/tls.html#tls_new_tls_tlssocket_socket_options

@@ -111,3 +111,3 @@ * @property {IdGenerator} idGenerator Generator to produce document ids

socketFactory: new SocketFactory(),
ssl: true
ssl: properties.ssl || true
}, properties, {

@@ -124,2 +124,3 @@ dbUser: properties.user || properties.dbUser,

endpoint.port = endpoint.socket || endpoint.port ? endpoint.port : 33060;
endpoint.auth = properties.ssl || endpoint.socket ? 'PLAIN' : 'MYSQL41';
});

@@ -148,3 +149,11 @@

function _authenticate () {
const AuthMethod = Auth.get(this._properties.authMethod || 'MYSQL41');
// This is a workaround because on MySQL 5.7.x, `CapabilitiesGet` over Unix Domain Sockets reports
// no support for `PLAIN`, while `PLAIN` is in fact supported.
const authMechanisms = this._serverCapabilities['authentication.mechanisms'].concat('PLAIN');
if (authMechanisms.indexOf(this._properties.auth) === -1) {
return Promise.reject(new Error('Authentication mechanism is not supported by the server'));
}
const AuthMethod = Auth.get(this._properties.auth);
const auth = new AuthMethod(this._properties);

@@ -248,2 +257,3 @@

this._properties.socket = this._properties.endpoints[index].socket;
this._properties.auth = this._properties.auth || (this._properties.ssl ? 'PLAIN' : this._properties.endpoints[index].auth);
}

@@ -286,7 +296,7 @@

*
* @param {string} schema - Name of the schema (database)
* @param {string} name - Name of the schema (database)
* @returns {Schema}
*/
Session.prototype.getSchema = function (schema) {
return new Schema(this, schema);
Session.prototype.getSchema = function (name) {
return schema(this, name);
};

@@ -391,3 +401,3 @@

Session.prototype.inspect = function (depth) {
const available = ['dbUser', 'host', 'port', 'socket', 'ssl'];
const available = ['auth', 'dbUser', 'host', 'port', 'socket', 'ssl'];

@@ -394,0 +404,0 @@ return Object.keys(this._properties).reduce((result, key) => {

@@ -34,2 +34,3 @@ /*

* Build a raw query wrapper.
* @constructor
*/

@@ -36,0 +37,0 @@ function Statement (client, query, args) {

@@ -29,184 +29,233 @@ /*

const DatabaseObject = require('./DatabaseObject');
const LinkOperations = require('./LinkOperations');
const TableDelete = require('./TableDelete');
const TableInsert = require('./TableInsert');
const TableSelect = require('./TableSelect');
const TableUpdate = require('./TableUpdate');
const databaseObject = require('./DatabaseObject');
const linking = require('./Linking');
const parseFlexibleParamList = require('./Util/parseFlexibleParamList');
const util = require('util');
const tableDelete = require('./TableDelete');
const tableInsert = require('./TableInsert');
const tableSelect = require('./TableSelect');
const tableUpdate = require('./TableUpdate');
/**
* Table object
*
* Usually you shouldn't create an instance of this but ask a Schema for it
*
* @param {Session} session
* @param {Schema} schema
* @param {String} table
* @param {String} alias
* @param {object} links
* @constructor
* @extends DatabaseObject
* Table factory.
* @module Table
* @mixes DatabaseObject
* @mixes Linking
*/
function Table (session, schema, table, alias, links) {
DatabaseObject.call(this, session, schema);
this._table = table;
this._links = links || {};
this._alias = alias;
}
/**
* @private
* @alias module:Table
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} name - table name
* @param {string} alias - table alias
* @param {Object} links - table join links
* @returns {Table}
*/
function Table (session, schema, name, alias, links) {
const state = Object.assign({}, { links: {} }, { alias, links, name, schema });
module.exports = Table;
return Object.assign({}, databaseObject(session), linking(), {
/**
* Set an alias for link operation using the table.
* @function
* @name module:Table#as
* @param {string} alias - new table alias
* @returns {Table} The entity instance.
*/
as (alias) {
return Table(this.getSession(), this.getSchema(), this.getName(), alias, this.getLinks());
},
util.inherits(Table, DatabaseObject);
LinkOperations.applyTo(Table);
/**
* Retrieve the total number of rows in the table.
* @function
* @name module:Table#count
* @returns {Promise.<number>}
*/
// TODO(Rui): extract method into a proper aspect (to be used on Collection and Table).
count () {
const schema = Table.escapeIdentifier(this.getSchema().getName());
const table = Table.escapeIdentifier(this.getName());
Table.escapeIdentifier = function (ident) {
return '`' + (ident || '').replace('`', '``') + '`';
};
let count = 0;
/**
* Get the name of this table
* @returns {string}
*/
Table.prototype.getName = function () {
return this._table;
};
return this
.getSession()
._client
.sqlStmtExecute(`SELECT COUNT(*) FROM ${schema}.${table}`, [], row => { count = row[0]; })
.then(() => count);
},
/**
* Verifies this table exists
* @returns {Promise<boolean>}
*/
Table.prototype.existsInDatabase = function () {
const query = 'SELECT COUNT(*) cnt FROM information_schema.TABLES WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';
const args = ['def', this._schema.getName(), this._table];
let status = false;
/**
* Create operation to delete rows from a table.
* @function
* @name module:Table#delete
* @param {SearchConditionStr} [expr] - filtering criteria
* @example
* // delete all rows from a table
* table.delete('true')
*
* // delete rows that match a given criteria
* table.delete('`name` == "foobar"')
* @returns {TableDelete} The operation instance.
*/
delete (expr) {
return tableDelete(this.getSession(), this.getSchema(), this.getName(), expr);
},
return this._session._client
.sqlStmtExecute(query, args, (found) => { status = !!found.length; })
.then(() => status);
};
/**
* Check if the table exists in the database.
* @function
* @name module:Table#existsInDatabase
* @returns {Promise.<boolean>}
*/
// TODO(Rui): extract method into a proper aspect (to be used on Collection, Schema and Table).
existsInDatabase () {
const query = 'SELECT COUNT(*) cnt FROM information_schema.TABLES WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';
const args = ['def', this.getSchema().getName(), this.getName()];
let status = false;
/**
* Checks whether this table is a View
* @returns {Promise<boolean>}
*/
Table.prototype.isView = function () {
const query = 'SELECT COUNT(*) cnt FROM information_schema.VIEWS WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';
const args = ['def', this._schema.getName(), this._table];
let status = false;
return this
.getSession()
._client
.sqlStmtExecute(query, args, (found) => { status = !!found.length; })
.then(() => status);
},
/**
* Retrieve the table name.
* @function
* @name module:Table#getName
* @returns {string}
*/
getName () {
return state.name;
},
return this._session._client
.sqlStmtExecute(query, args, (found) => { status = !!found.length; })
.then(() => status);
};
/**
* Retrieve the schema associated to the table.
* @function
* @name module:Table#getSchema
* @returns {Schema}
*/
getSchema () {
return state.schema;
},
/**
* Select rows from a table
* @param {...string|string[]} [ProjectedSearchExpr] - columns to be projected
* @throws {Error} When an expression is invalid.
* @example
* // all columns should be projected
* const selection = table.select()
*
* // arguments as columns to be projected
* const selection = table.select('foo', 'bar')
*
* // array of columns to be projected
* const selection = table.select(['foo', 'bar'])
* @returns {TableSelect}
*/
Table.prototype.select = function () {
const fields = parseFlexibleParamList(Array.prototype.slice.call(arguments));
/**
* Retrieve the table metadata.
* @function
* @name module:Table#inspect
* @returns {Object} An object containing the relevant metadata.
*/
inspect () {
return { schema: this.getSchema().getName(), table: this.getName() };
},
return new TableSelect(this._session, this._schema, this._table, fields);
};
/**
* Create operation to insert rows in the table.
* @function
* @name module:Table#insert
* @param {...string|string[]|Object} fields - column names or column-value object
* @throws {Error} When the input type is invalid.
* @example
* // arguments as column names
* table.insert('foo', 'bar')
*
* // array of column names
* table.insert(['foo', 'bar'])
*
* // object with column name and value
* table.insert({ foo: 'baz', bar: 'qux' })
* @returns {TableInsert} The operation instance.
*/
insert () {
if (!Array.isArray(arguments[0]) && typeof arguments[0] !== 'string') {
const fields = arguments[0];
/**
* Insert rows
* @param {...string|string[]|Object} TableField - column names or column-value object
* @throws {Error} When the input type is invalid.
* @example
* // arguments as column names
* table.insert('foo', 'bar')
*
* // array of column names
* table.insert(['foo', 'bar'])
*
* // object with column name and value
* table.insert({ foo: 'baz', bar: 'qux' })
* @returns {TableInsert}
*/
Table.prototype.insert = function () {
if (!Array.isArray(arguments[0]) && typeof arguments[0] !== 'string') {
const fields = arguments[0];
if (typeof fields !== 'object') {
throw new Error('fields must be provided as multiple Strings, an Array or an Object with the column name and value');
}
if (typeof fields !== 'object') {
throw new Error('fields must be provided as multiple Strings, an Array or an Object with the column name and value');
}
const columns = Object.keys(fields);
const values = columns.map(column => fields[column]);
const columns = Object.keys(fields);
const values = columns.map(column => fields[column]);
return tableInsert(this.getSession(), this.getSchema(), this.getName(), columns).values(values);
}
return (new TableInsert(this._session, this._schema, this._table, columns)).values(values);
}
const columns = parseFlexibleParamList(Array.prototype.slice.call(arguments));
const columns = parseFlexibleParamList(Array.prototype.slice.call(arguments));
return tableInsert(this.getSession(), this.getSchema(), this.getName(), columns);
},
return new TableInsert(this._session, this._schema, this._table, columns);
};
/**
* Check whether the table is a view.
* @function
* @name module:Table#isView
* @returns {Promise.<boolean>}
*/
isView () {
const query = 'SELECT COUNT(*) cnt FROM information_schema.VIEWS WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';
const args = ['def', this.getSchema().getName(), this.getName()];
let status = false;
/**
* Run update operations
* @param {String} expr Expression
* @returns {TableUpdate}
*/
Table.prototype.update = function (expr) {
return new TableUpdate(this._session, this._schema, this._table, expr);
};
return this
.getSession()
._client
.sqlStmtExecute(query, args, (found) => { status = !!found.length; })
.then(() => status);
},
/**
* Create operation to delete rows from a table.
* @param {string} expr Expression
* @example
* // delete all rows from a table
* table.delete('true')
*
* // delete rows that match a given criteria
* table.delete('`name` == "foobar"')
* @returns {TableDelete}
*/
Table.prototype.delete = function (expr) {
return new TableDelete(this._session, this._schema, this._table, expr);
};
/**
* Create operation to select rows from the table.
* @function
* @name module:Table#select
* @param {...string|string[]} [expr] - columns to be projected
* @throws {Error} When an expression is invalid.
* @example
* // all columns should be projected
* const selection = table.select()
*
* // arguments as columns to be projected
* const selection = table.select('foo', 'bar')
*
* // array of columns to be projected
* const selection = table.select(['foo', 'bar'])
* @returns {TableSelect} The operation instance.
*/
select () {
const fields = parseFlexibleParamList(Array.prototype.slice.call(arguments));
/**
* Get number of rows in this Table
*
* @returns {Promise.<Number>}
*/
Table.prototype.count = function () {
const schema = Table.escapeIdentifier(this._schema.getName());
const table = Table.escapeIdentifier(this._table);
return tableSelect(this.getSession(), this.getSchema(), this.getName(), fields);
},
let count = 0;
/**
* Create operation to update rows in the table.
* @function
* @name module:Table#update
* @param {string} [expr] - filtering criteria
* @example
* // update all rows in a table
* table.update('true').set('name', 'foo')
* table.update().where('true').set('name', 'foo')
*
* // update rows that match a given criteria
* table.update().where('`name` == "foo"').set('name', 'bar')
* @returns {TableUpdate} The operation instance.
*/
update (expr) {
return tableUpdate(this.getSession(), this.getSchema(), this.getName(), expr);
}
});
}
return this._session._client
.sqlStmtExecute(`SELECT COUNT(*) FROM ${schema}.${table}`, [], row => { count = row[0]; })
.then(() => count);
};
/**
* Set an alias for link operation
*
* @param {String} alias
* @returns {Table}
* Internal utility function.
*/
Table.prototype.as = function (alias) {
return new Table(this._session, this._schema, this._table, alias, this._links);
// TODO(Rui): refactor somehow.
Table.escapeIdentifier = function (ident) {
return '`' + (ident || '').replace('`', '``') + '`';
};
Table.prototype.inspect = function () {
return { schema: this._schema.getName(), table: this._table };
};
module.exports = Table;

@@ -29,61 +29,68 @@ /*

const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Result = require('./Result');
const util = require('util');
const binding = require('./Binding');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
/**
* Operation to delete rows from a table.
* @param {Session} session
* @param {Schema} schema
* @param {Table} table
* @param {String} expression
* @constructor
* TableDelete factory.
* @module TableDelete
* @mixes Filtering
* @mixes Limiting
*/
function TableDelete (session, schema, table, query) {
BaseQuery.call(this);
this._session = session;
this._schema = schema;
this._table = table;
this._query = query;
this._bounds = {};
}
util.inherits(TableDelete, BaseQuery);
module.exports = TableDelete;
/**
* Bind statement query parameter values.
* @returns {TableDelete} The operation instance.
* @private
* @alias module:TableDelete
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} table - table name
* @param {string} [criteria] - filtering criteria expression
* @returns {TableDelete}
*/
TableDelete.prototype.bind = function (bind) {
this._bounds = Object.assign({}, this._bounds, bind);
function TableDelete (session, schema, table, criteria) {
let state = Object.assign({ }, { session, schema, table });
return this;
};
return Object.assign({}, filtering({ criteria }), binding(), limiting(), {
/**
* Execute delete operation.
* @function
* @name module:TableDelete#execute
* @return {Promise.<Result>}
*/
execute () {
if (typeof this.getCriteria() !== 'string' || this.getCriteria().trim().length < 1) {
return Promise.reject(new Error('delete needs a valid condition'));
}
/**
* Set a condition for picking which rows to delete.
* @param {string} query - filtering condition
* @returns {TableDelete} The operation instance.
*/
TableDelete.prototype.where = function (query) {
this._query = query;
return state
.session
._client
.crudRemove(state.schema.getName(), state.table, Client.dataModel.TABLE, this.getCriteria(), this.getLimit(), this.getBindings()).then(state => new Result(state));
},
return this;
};
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:TableDelete#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'TableDelete';
},
/**
* Execute delete operation.
* @return {Promise.<Result>}
*/
TableDelete.prototype.execute = function () {
if (typeof this._query !== 'string' || this._query.trim().length < 1) {
return Promise.reject(new Error('delete needs a valid condition'));
}
/**
* Add <code>WHERE</code> clause (set the criteria for picking which rows to delete).
* @function
* @name module:TableDelete#where
* @param {string} criteria - filtering criteria
* @returns {TableDelete} The operation instance.
*/
where (criteria) {
return this.setCriteria(criteria);
}
});
}
return this._session._client
.crudRemove(this._schema.getName(), this._table, Client.dataModel.TABLE, this._query, this._limit, this._bounds).then(state => new Result(state));
};
module.exports = TableDelete;
/*
* Copyright (c) 2015, 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -27,90 +27,115 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

"use strict";
'use strict';
const Client = require('../Protocol/Client'),
Table = require('./Table'),
Result = require('./Result');
const Client = require('../Protocol/Client');
const Result = require('./Result');
const parseFlexibleParamList = require('./Util/parseFlexibleParamList');
/**
*
* @param {Session} session
* @param {Schema} schema
* @param {String} table name
* @param {Array<string>} fields
* @constructor
* TableInsert factory.
* @module TableInsert
* @mixes Filtering
* @mixes Limiting
*/
function TableInsert(session, schema, table, fields) {
this._session = session;
this._schema = schema;
this._table = table;
this._fields = fields;
this._rows = [];
}
module.exports = TableInsert;
/**
* Set values for a row
*
* Individual values have to be provided as arguments or in form of a single array
*
* Both ways are shown below:
*
* <pre>table.
* insert(["col1", "col2"]).
* values("row 1, value 1", "row 1 value 2").
* values(["row 2, value 1", "row 2, value 2"]).
* execute();</pre>
*
* @private
* @alias module:TableInsert
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} table - table name
* @param {Array.<string>} fields - projection
* @returns {TableInsert}
*/
/**
* Insert rows
* @param {...string|string[]} ExprOrLiteral - column values
* @throws {Error} When there is a mismatch with the number columns in the query.
* @example
* // arguments as column values
* table.insert('foo', 'bar').values('baz', 'qux')
* table.insert(['foo', 'bar']).values('baz', 'qux')
*
* // array of column values
* table.insert('foo', 'bar').values(['baz', 'qux'])
* table.insert(['foo', 'bar']).values(['baz', 'qux'])
*
* // comma-separated string with column values
* table.insert('foo', 'bar').values('baz, qux'])
* table.insert(['foo', 'bar']).values('baz, qux')
*
* // chaining multiple inserts
* table.insert('foo', 'bar')
* .values(['baz', 'qux'])
* .values(['quux', 'biz'])
* .values('foo, bar')
* @returns {TableInsert}
*/
TableInsert.prototype.values = function () {
const values = parseFlexibleParamList(Array.prototype.slice.call(arguments));
function TableInsert (session, schema, table, fields) {
let state = Object.assign({ rows: [] }, { session, schema, table, fields });
if (this._fields.length !== values.length) {
throw new Error(`Mismatch in column count. ${this._fields.length} fields listed, ${values.length} values provided`);
}
return {
/**
* Execute the insert operation.
* @function
* @name module:TableInsert#execute
* @returns {Promise.<Result>}
*/
execute () {
const columns = state.fields.map(field => ({ name: field }));
const rows = state.rows;
// FIXME(ruiquelhas): this is creating a nested array, which is probably unnecessary.
this._rows.push(values);
return this;
};
return state
.session
._client
.crudInsert(state.schema.getName(), state.table, Client.dataModel.TABLE, { columns, rows })
.then(state => new Result(state));
},
/**
*
* @returns {Promise.<Result>}
*/
TableInsert.prototype.execute = function () {
// FIXME(ruiquelhas): shouldn't this be a concern of `crudInsert` itself?
var projection = this._fields.map(function (field) {
return { name: field };
});
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:TableInsert#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'TableInsert';
},
return this._session._client.crudInsert(this._schema.getName(), this._table, Client.dataModel.TABLE, this._rows, projection).then(state => new Result(state));
};
/**
* Retrieve the set of columns.
* @function
* @name module:TableInsert#getFields
* @returns {Array.<string>}
*/
getFields () {
return state.fields;
},
/**
* Retrieve the set of row values.
* @function
* @name module:TableInsert#getRows
* @returns {Promise.<Result>}
*/
getRows () {
return state.rows;
},
/**
* Set row values.
* @function
* @name module:TableInsert:values
* @param {...string|string[]} ExprOrLiteral - column values
* @throws {Error} When there is a mismatch with the number columns in the query.
* @example
* // arguments as column values
* table.insert('foo', 'bar').values('baz', 'qux')
* table.insert(['foo', 'bar']).values('baz', 'qux')
*
* // array of column values
* table.insert('foo', 'bar').values(['baz', 'qux'])
* table.insert(['foo', 'bar']).values(['baz', 'qux'])
*
* // comma-separated string with column values
* table.insert('foo', 'bar').values('baz, qux'])
* table.insert(['foo', 'bar']).values('baz, qux')
*
* // chaining multiple inserts
* table.insert('foo', 'bar')
* .values(['baz', 'qux'])
* .values(['quux', 'biz'])
* .values('foo, bar')
* @returns {TableInsert} The operation instance
*/
values () {
const values = parseFlexibleParamList(Array.prototype.slice.call(arguments));
if (state.fields.length !== values.length) {
throw new Error(`Mismatch in column count. ${state.fields.length} fields listed, ${values.length} values provided`);
}
state.rows.push(values);
return this;
}
};
}
module.exports = TableInsert;
/*
* Copyright (c) 2015, 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*

@@ -29,125 +29,179 @@ * MySQL Connector/Node.js is licensed under the terms of the GPLv2

const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Column = require('./Column');
const Result = require('./Result');
const binding = require('./Binding');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
const locking = require('./Locking');
const parseExpressionInputs = require('./Util/parseExpressionInputs');
const parseFlexibleParamList = require('./Util/parseFlexibleParamList');
const util = require('util');
/**
*
* @param {Session} session
* @param {Schema} schema
* @param {Table} table
* @param {Array.<String>} projection
* @constructor
* TableSelect factory.
* @module TableSelect
* @mixes Filtering
* @mixes Binding
* @mixes Limiting
* @mixes Locking
*/
/**
* @private
* @alias module:TableSelect
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} table - table name
* @param {Array.<string>} projection - projection expressions
* @returns {TableSelect}
*/
function TableSelect (session, schema, table, projection) {
BaseQuery.call(this);
let state = Object.assign({ joins: [] }, { session, schema, table, projection });
this._session = session;
this._schema = schema;
this._table = table;
this._projection = projection;
this._where = null;
this._groupby = null;
this._having = null;
this._orderby = null;
this._bounds = {};
this._joins = [];
}
return Object.assign({}, filtering(), binding(), limiting(), locking(), {
/**
* Execute find operation.
* @function
* @name module:TableSelect#execute
* @param {documentCallback} [rowcb]
* @param {Array<Column>} [metacb]
* @return {Promise.<Result>}
*/
execute (rowcb, metacb) {
return state
.session
._client
.crudFind(state.session, state.schema.getName(), state.table, Client.dataModel.TABLE, parseExpressionInputs(state.projection), this.getCriteria(), state.groupby, state.having, state.orderby, this.getLimit(), rowcb, Column.metaCB(metacb), this.getBindings(), state.joins, this.getLockingMode())
.then(state => new Result(state));
},
util.inherits(TableSelect, BaseQuery);
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:TableSelect#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'TableSelect';
},
module.exports = TableSelect;
/**
* Retrieve the grouping options.
* @function
* @name module:TableSelect#getGroupBy
* @returns {object} An object with the grouping options.
*/
getGroupBy () {
return state.groupby;
},
/**
* Add where clause
* @param expr
* @returns {TableSelect}
*/
TableSelect.prototype.where = function (expr) {
this._where = expr;
return this;
};
/**
* Retrieve the sorting options.
* @function
* @name module:TableSelect#getOrderBy
* @returns {object} An object with the sorting options.
*/
getOrderBy () {
return state.orderby;
},
/**
* Add <code>GROUP BY</code> clause.
* @param {...string|string[]} [GroupByExprStr] - columns to group by
* @throws {Error} When an expression is invalid.
* @example
* // arguments as columns group by
* const query = table.select('foo', 'bar').groupBy('foo asc', 'bar desc')
*
* // array of columns to group by
* const query = table.select('foo', 'bar').groupBy(['foo asc', 'bar desc'])
* @returns {TableSelect}
*/
TableSelect.prototype.groupBy = function () {
this._groupby = parseFlexibleParamList(Array.prototype.slice.call(arguments));
return this;
};
/**
* Retrieve the projection map.
* @function
* @name module:TableSelect#getProjection
* @returns {object} An object containing the project map.
*/
getProjection () {
return state.projection;
},
/**
* Add having clause
* @param expr
* @returns {TableSelect}
*/
TableSelect.prototype.having = function (expr) {
this._having = expr;
return this;
};
/**
* Build a view for the operation.
* @function
* @name module:TableSelect#getViewDefinition
* @returns {string} The view SQL string.
*/
getViewDefinition () {
// TODO(rui.quelhas): check if this is the best place to escape the interpolated properties
// console.log(Table, new Table(), Table.escapeIdentifier);
// let retval = "SELECT " + state.projection.join(", ") + " FROM " + Table.escapeIdentifier(state.schema.getName()) + '.' + Table.escapeIdentifier(state.table);
let view = `SELECT ${state.projection.join(', ')} FROM ${state.schema.getName()}.${state.table}`;
/**
* Add <code>ORDER BY</code> clause.
* @param {...string|string[]} [SortExprStr] - columns (and direction) to sort
* @throws {Error} When an expression is invalid.
* @example
* // arguments as columns (and direction) to sort
* const query = table.select('foo', 'bar').orderBy('foo asc', 'bar desc')
*
* // array of columns (and direction) to sort
* const query = table.select('foo', 'bar').orderBy(['foo asc', 'bar desc'])
* @returns {TableSelect}
*/
TableSelect.prototype.orderBy = function () {
this._orderby = parseFlexibleParamList(Array.prototype.slice.call(arguments));
return this;
};
if (this.getCriteria()) {
view = `${view} WHERE ${this.getCriteria()}`;
}
TableSelect.prototype.bind = function (bind) {
Object.assign(this._bounds, bind);
return this;
};
if (state.orderby) {
view = `${view} ORDER BY ${state.orderby.join(', ')}`;
}
/**
* Execute find operation
* @param {documentCallback} [rowcb]
* @param {Array<Column>} [metacb]
* @return {Promise.<Result>}
*/
TableSelect.prototype.execute = function (rowcb, metacb) {
return this
._session
._client
.crudFind(this._session, this._schema.getName(), this._table, Client.dataModel.TABLE, parseExpressionInputs(this._projection), this._where, this._groupby, this._having, this._orderby, this._limit, rowcb, Column.metaCB(metacb), this._bounds, this._joins)
.then(state => new Result(state));
};
return view;
},
TableSelect.prototype.getViewDefinition = function () {
// TODO(rui.quelhas): check if this is the best place to escape the interpolated properties
// console.log(Table, new Table(), Table.escapeIdentifier);
// let retval = "SELECT " + this._projection.join(", ") + " FROM " + Table.escapeIdentifier(this._schema.getName()) + '.' + Table.escapeIdentifier(this._table);
let view = `SELECT ${this._projection.join(', ')} FROM ${this._schema.getName()}.${this._table}`;
/**
* Add <code>GROUP BY</code> clause (set the grouping options of the result set).
* @function
* @name module:TableSelect#groupBy
* @param {...string|string[]} [GroupByExprStr] - columns to group by
* @throws {Error} When an expression is invalid.
* @example
* // arguments as columns group by
* const query = table.select('foo', 'bar').groupBy('foo asc', 'bar desc')
*
* // array of columns to group by
* const query = table.select('foo', 'bar').groupBy(['foo asc', 'bar desc'])
* @returns {TableSelect} The operation instance
*/
groupBy () {
state.groupby = parseFlexibleParamList(Array.prototype.slice.call(arguments));
if (this._where) {
view = `${view} WHERE ${this._where}`;
}
return this;
},
if (this._orderby) {
view = `${view} ORDER BY ${this._orderby.join(', ')}`;
}
/**
* Add <code>HAVING</code> clause.
* @function
* @name module:TableSelect#having
* @param {SearchConditionStr} expr - filtering criteria
* @returns {TableSelect} The operation instance
*/
having (expr) {
state.having = expr;
return view;
};
return this;
},
/**
* Add <code>ORDER BY</code> clause (set the order options of the result set).
* @function
* @name module:TableSelect#orderBy
* @param {...string|string[]} [SortExprStr] - columns (and direction) to sort
* @throws {Error} When an expression is invalid.
* @example
* // arguments as columns (and direction) to sort
* const query = table.select('foo', 'bar').orderBy('foo asc', 'bar desc')
*
* // array of columns (and direction) to sort
* const query = table.select('foo', 'bar').orderBy(['foo asc', 'bar desc'])
* @returns {TableSelect} The operation instance
*/
orderBy () {
state.orderby = parseFlexibleParamList(Array.prototype.slice.call(arguments));
return this;
},
/**
* Add <code>WHERE</code> clause (set the criteria for which rows to pick).
* @function
* @name module:TableSelect#where
* @param {SearchConditionStr} criteria - filtering criteria
* @returns {TableSelect} The operation instance
*/
where (criteria) {
return this.setCriteria(criteria);
}
});
}
module.exports = TableSelect;

@@ -29,83 +29,91 @@ /*

const BaseQuery = require('./BaseQuery');
const Client = require('../Protocol/Client');
const Expressions = require('../Expressions');
const Result = require('./Result');
const util = require('util');
const binding = require('./Binding');
const filtering = require('./Filtering');
const limiting = require('./Limiting');
/**
* Operation to update rows in a table.
* @param {Session} session
* @param {Schema} schema
* @param {Table} table
* @constructor
* TableUpdate factory.
* @module TableUpdate
* @mixes Filtering
* @mixes Binding
* @mixes Limiting
*/
function TableUpdate (session, schema, table, query) {
BaseQuery.call(this);
this._session = session;
this._schema = schema;
this._table = table;
this._operations = [];
this._query = query;
this._bounds = {};
}
util.inherits(TableUpdate, BaseQuery);
module.exports = TableUpdate;
/**
* Bind statement query parameter values.
* @returns {TableDelete} The operation instance.
* @private
* @alias module:TableUpdate
* @param {Session} session - session to bind
* @param {Schema} schema - associated schema
* @param {string} table - table name
* @param {string} criteria - criteria expression
* @returns {TableUpdate}
*/
TableUpdate.prototype.bind = function (bind) {
this._bounds = Object.assign({}, this._bounds, bind);
function TableUpdate (session, schema, table, criteria) {
let state = Object.assign({ operations: [] }, { session, schema, table });
return this;
};
return Object.assign({}, filtering({ criteria }), binding(), limiting(), {
/**
* Execute update operation.
* @function
* @name module:TableUpdate#execute
* @return {Promise.<Result>}
*/
execute () {
if (typeof this.getCriteria() !== 'string' || this.getCriteria().trim().length < 1) {
return Promise.reject(new Error('update needs a valid condition'));
}
/**
* Add a field to be updated
* @param {string} field Name of the field
* @param {string} expr Expression
* @returns {TableUpdate} Returns itself
*/
TableUpdate.prototype.set = function (field, expr) {
this._operations.push({
operation: 1,
source: {
name: field
return state
.session
._client
.crudModify(state.schema.getName(), state.table, Client.dataModel.TABLE, this.getCriteria(), state.operations, this.getLimit(), this.getBindings())
.then(state => new Result(state));
},
value: Expressions.literalOrParsedExpression(expr)
});
return this;
};
/**
* Retrieve the class name (to avoid duck typing).
* @function
* @name module:TableUpdate#getClassName
* @returns {string} The "class" name.
*/
getClassName () {
return 'TableUpdate';
},
/**
* Set a condition for picking which rows to delete.
* @param {string} query - filtering condition
* @returns {TableUpdate} The operation instance
*/
TableUpdate.prototype.where = function (query) {
this._query = query;
/**
* Add a field to be updated with a new value.
* @function
* @name module:TableUpdate#set
* @param {string} field - field name
* @param {string} expr - value expression
* @returns {TableUpdate} The operation instance
*/
set (field, expr) {
state.operations.push({
operation: 1,
source: {
name: field
},
value: Expressions.literalOrParsedExpression(expr)
});
return this;
};
return this;
},
/**
* Execute update operation.
* @return {Promise.<Result>}
*/
TableUpdate.prototype.execute = function () {
if (typeof this._query !== 'string' || this._query.trim().length < 1) {
return Promise.reject(new Error('update needs a valid condition'));
}
/**
* Add <code>WHERE</code> clause (set the criteria for picking which rows to update).
* @function
* @name module:TableUpdate#where
* @param {string} criteria - filtering condition
* @returns {TableUpdate} The operation instance
*/
where (criteria) {
return this.setCriteria(criteria);
}
});
}
return this
._session
._client
.crudModify(this._schema.getName(), this._table, Client.dataModel.TABLE, this._query, this._operations, this._limit, this._bounds)
.then(state => new Result(state));
};
module.exports = TableUpdate;

@@ -30,3 +30,3 @@ /*

module.exports = function (inputs) {
return inputs.map(function (field) {
return (inputs || []).map(function (field) {
const splitted = field.split(/\sas\s/i);

@@ -48,2 +48,2 @@

});
};
};

@@ -30,2 +30,3 @@ /*

const parseAddressList = require('./parseAddressList');
const parseAuthenticationMechanism = require('./parseAuthenticationMechanism');
const parseSchema = require('./parseSchema');

@@ -72,2 +73,3 @@ const parseSecurityOptions = require('./parseSecurityOptions');

return {
auth: parseAuthenticationMechanism(base[5]),
dbUser: userinfo.username,

@@ -74,0 +76,0 @@ dbPassword: userinfo.password,

@@ -1,2 +0,2 @@

/* parser generated by jison 0.4.15 */
/* parser generated by jison 0.4.17 */
/*

@@ -75,8 +75,8 @@ Returns a Parser object of the following structure:

var parser = (function(){
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,22],$V1=[1,14],$V2=[1,15],$V3=[1,6],$V4=[1,7],$V5=[1,23],$V6=[1,8],$V7=[1,12],$V8=[1,20],$V9=[1,18],$Va=[1,24],$Vb=[1,17],$Vc=[1,25],$Vd=[1,28],$Ve=[1,29],$Vf=[1,30],$Vg=[1,31],$Vh=[1,32],$Vi=[1,33],$Vj=[1,34],$Vk=[1,35],$Vl=[1,36],$Vm=[1,37],$Vn=[1,38],$Vo=[1,39],$Vp=[1,40],$Vq=[1,41],$Vr=[1,42],$Vs=[4,21,22,26,27,28,29,30,31,32,33,34,35,36,37,38,39,44,47,53],$Vt=[1,52],$Vu=[1,53],$Vv=[1,62],$Vw=[1,59],$Vx=[1,60],$Vy=[1,61],$Vz=[1,63],$VA=[8,10,11,13,14,15,16,25,42,50,52,58,61],$VB=[26,47],$VC=[58,60],$VD=[4,19,21,22,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,44,47,53],$VE=[60,61,64,65,66,67],$VF=[4,26,44,47,53];
var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,24],$V1=[1,15],$V2=[1,16],$V3=[1,17],$V4=[1,6],$V5=[1,7],$V6=[1,25],$V7=[1,8],$V8=[1,9],$V9=[1,13],$Va=[1,22],$Vb=[1,20],$Vc=[1,21],$Vd=[1,19],$Ve=[1,26],$Vf=[1,29],$Vg=[1,30],$Vh=[1,31],$Vi=[1,32],$Vj=[1,33],$Vk=[1,34],$Vl=[1,35],$Vm=[1,36],$Vn=[1,37],$Vo=[1,38],$Vp=[1,39],$Vq=[1,40],$Vr=[1,41],$Vs=[1,42],$Vt=[1,43],$Vu=[1,44],$Vv=[4,23,24,25,30,31,32,33,34,35,36,37,38,39,40,41,42,43,48,51,58],$Vw=[1,55],$Vx=[1,56],$Vy=[1,59],$Vz=[1,69],$VA=[1,66],$VB=[1,67],$VC=[1,68],$VD=[1,70],$VE=[1,77],$VF=[8,10,11,12,14,15,16,17,18,29,46,54,56,64,67],$VG=[1,79],$VH=[30,51],$VI=[64,66],$VJ=[4,21,23,24,25,30,31,32,33,34,35,36,37,38,39,40,41,42,43,46,48,51,58],$VK=[51,58],$VL=[1,92],$VM=[2,57],$VN=[66,67,70,71,72,73],$VO=[4,30,48,51,58];
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"Input":3,"EOF":4,"Expression":5,"StringOrNumber":6,"string":7,"Number":8,"Literal":9,"true":10,"false":11,"FunctionCall":12,"?":13,":":14,"StringLiteral":15,"@":16,"SQLVariable":17,"column":18,".":19,"BinaryOperator":20,"like":21,"not":22,"DocPath":23,"JSONExpression":24,"(":25,")":26,"||":27,"&&":28,"==":29,"!=":30,"+":31,"-":32,"*":33,"/":34,"%":35,"<":36,">":37,"<=":38,">=":39,"Field":40,"@.":41,"[":42,"Index":43,"]":44,"FunctionName":45,"FunctionArgs":46,",":47,"DocPathElement":48,"DocPathElements":49,"$":50,"JSONDocument":51,"{":52,"}":53,"int":54,"DEC":55,"HEX":56,"OCT":57,"`":58,"column_quoted":59,"NON_ESCAPED":60,"QUOTE":61,"string_quoted":62,"string_quoted_char":63,"HEX_ESCAPE":64,"OCT_ESCAPE":65,"CHAR_ESCAPE":66,"NAME":67,"$accept":0,"$end":1},
terminals_: {2:"error",4:"EOF",8:"Number",10:"true",11:"false",13:"?",14:":",15:"StringLiteral",16:"@",17:"SQLVariable",19:".",21:"like",22:"not",25:"(",26:")",27:"||",28:"&&",29:"==",30:"!=",31:"+",32:"-",33:"*",34:"/",35:"%",36:"<",37:">",38:"<=",39:">=",41:"@.",42:"[",43:"Index",44:"]",47:",",50:"$",52:"{",53:"}",55:"DEC",56:"HEX",57:"OCT",58:"`",60:"NON_ESCAPED",61:"QUOTE",64:"HEX_ESCAPE",65:"OCT_ESCAPE",66:"CHAR_ESCAPE",67:"NAME"},
productions_: [0,[3,1],[3,2],[6,1],[6,1],[9,1],[9,1],[9,1],[5,1],[5,1],[5,1],[5,2],[5,2],[5,1],[5,3],[5,5],[5,3],[5,3],[5,4],[5,1],[5,1],[5,3],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[40,11],[12,4],[45,1],[45,3],[46,0],[46,1],[46,3],[48,2],[48,3],[48,3],[49,1],[49,2],[23,2],[24,1],[24,5],[51,9],[54,1],[54,1],[54,1],[18,3],[59,1],[59,2],[7,3],[62,1],[62,2],[63,1],[63,1],[63,1],[63,1],[63,1]],
symbols_: {"error":2,"Input":3,"EOF":4,"Expression":5,"StringOrNumber":6,"string":7,"Number":8,"Literal":9,"true":10,"false":11,"null":12,"FunctionCall":13,"?":14,":":15,"StringLiteral":16,"!":17,"@":18,"SQLVariable":19,"column":20,".":21,"BinaryOperator":22,"like":23,"not":24,"in":25,"argsList":26,"DocPath":27,"JSONExpression":28,"(":29,")":30,"||":31,"&&":32,"==":33,"!=":34,"+":35,"-":36,"*":37,"/":38,"%":39,"<":40,">":41,"<=":42,">=":43,"Field":44,"@.":45,"[":46,"Index":47,"]":48,"FunctionName":49,"FunctionArgs":50,",":51,"DocPathElement":52,"DocPathElements":53,"$":54,"Expressions":55,"{":56,"KeyValuePairs":57,"}":58,"KeyValuePair":59,"int":60,"DEC":61,"HEX":62,"OCT":63,"`":64,"column_quoted":65,"NON_ESCAPED":66,"QUOTE":67,"string_quoted":68,"string_quoted_char":69,"HEX_ESCAPE":70,"OCT_ESCAPE":71,"CHAR_ESCAPE":72,"NAME":73,"$accept":0,"$end":1},
terminals_: {2:"error",4:"EOF",8:"Number",10:"true",11:"false",12:"null",14:"?",15:":",16:"StringLiteral",17:"!",18:"@",19:"SQLVariable",21:".",23:"like",24:"not",25:"in",29:"(",30:")",31:"||",32:"&&",33:"==",34:"!=",35:"+",36:"-",37:"*",38:"/",39:"%",40:"<",41:">",42:"<=",43:">=",45:"@.",46:"[",47:"Index",48:"]",51:",",54:"$",56:"{",58:"}",61:"DEC",62:"HEX",63:"OCT",64:"`",66:"NON_ESCAPED",67:"QUOTE",70:"HEX_ESCAPE",71:"OCT_ESCAPE",72:"CHAR_ESCAPE",73:"NAME"},
productions_: [0,[3,1],[3,2],[6,1],[6,1],[9,1],[9,1],[9,1],[9,1],[5,1],[5,1],[5,1],[5,2],[5,2],[5,2],[5,1],[5,3],[5,5],[5,3],[5,3],[5,4],[5,3],[5,4],[5,3],[5,4],[5,1],[5,1],[5,3],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[22,1],[44,11],[13,4],[49,1],[49,3],[50,0],[50,1],[50,3],[52,2],[52,3],[52,3],[53,1],[53,2],[27,2],[26,3],[28,3],[28,3],[55,1],[55,3],[57,1],[57,3],[59,3],[60,1],[60,1],[60,1],[20,3],[65,1],[65,2],[7,2],[7,3],[68,1],[68,2],[69,1],[69,1],[69,1],[69,1],[69,1]],
performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {

@@ -114,3 +114,6 @@ /* this == yyval */

break;
case 10:
case 8:
this.$ = { type: 2, literal: Datatype.encodeScalar(null) }
break;
case 11:

@@ -120,3 +123,3 @@ this.$ = parser.addOrdinalPlaceholder();

break;
case 11:
case 12:

@@ -129,2 +132,13 @@ this.$ = parser.addNamedPlaceholder($$[$0]);

this.$ = {
type: 5,
operator: {
name: $$[$0-1],
param: [ $$[$0] ]
}
}
break;
case 15:
this.$ = {
type: 1,

@@ -137,3 +151,3 @@ identifier: {

break;
case 14:
case 16:

@@ -149,3 +163,3 @@ this.$ = {

break;
case 15:
case 17:

@@ -162,3 +176,3 @@ this.$ = {

break;
case 16:
case 18:

@@ -174,3 +188,3 @@ this.$ = {

break;
case 17:
case 19:

@@ -186,3 +200,3 @@ this.$ = {

break;
case 18:
case 20:

@@ -198,6 +212,50 @@ this.$ = {

break;
case 21: case 57:
case 21:
this.$ = {
type: 5,
operator: {
name: 'in',
param: [ $$[$0-2] ].concat($$[$0])
}
}
break;
case 22:
this.$ = {
type: 5,
operator: {
name: 'not_in',
param: [ $$[$0-3] ].concat($$[$0])
}
}
break;
case 23:
this.$ = {
type: 5,
operator: {
name: 'cont_in',
param: [ $$[$0-2], $$[$0] ]
}
}
break;
case 24:
this.$ = {
type: 5,
operator: {
name: 'not_cont_in',
param: [ $$[$0-3], $$[$0] ]
}
}
break;
case 27: case 69:
this.$ = $$[$0-1];
break;
case 36:
case 42:

@@ -215,30 +273,30 @@ this.$ = {

break;
case 37:
case 43:
this.$ = { name: $$[$0] }
break;
case 38:
case 44:
this.$ = { name: $$[$0-1], schema_name: $$[$0-2] }
break;
case 40:
case 46: case 59:
this.$ = [ $$[$0] ]
break;
case 41:
case 47: case 58: case 60:
this.$ = $$[$0-2]; this.$.push($$[$0]);
break;
case 42:
case 48:
this.$ = { type: 1, value: $$[$0] }
break;
case 43:
case 49:
this.$ = { type: 3, index: parseInt($$[$0-1]) };
break;
case 44:
case 50:
this.$ = { type: 4 }
break;
case 45:
case 51: case 57:
this.$ = [ $$[$0] ];
break;
case 46:
case 52:
this.$ = $$[$0-1]; this.$.push($$[$0]);
break;
case 47:
case 53:

@@ -253,12 +311,24 @@ this.$ = {

break;
case 54:
case 54: case 65:
this.$ = $$[$0-1]
break;
case 55: case 58: case 63: case 64:
case 55:
this.$ = { type: 7, object: { fld: $$[$0-1] } }
break;
case 56:
this.$ = { type: 8, array: { value: $$[$0-1] } };
break;
case 61:
this.$ = { key: $$[$0-2], value: $$[$0] }
break;
case 66: case 70: case 75: case 76:
this.$ = $$[$0];
break;
case 56: case 59:
case 67: case 71:
this.$ = $$[$0-1] + $$[$0];
break;
case 60: case 61: case 62:
case 68:
this.$ = '';
break;
case 72: case 73: case 74:
this.$ = parser.charUnescape($$[$0]);

@@ -268,4 +338,4 @@ break;

},
table: [{3:1,4:[1,2],5:3,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},{1:[3]},{1:[2,1]},{4:[1,26],20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr},o($Vs,[2,8]),o($Vs,[2,9]),o($Vs,[2,10]),{15:[1,43]},{17:[1,44]},o($Vs,[2,13],{19:[1,45]}),o($Vs,[2,19]),o($Vs,[2,20]),{5:46,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},o($Vs,[2,5]),o($Vs,[2,6]),o($Vs,[2,7]),{25:[1,47]},{59:48,60:[1,49]},{19:$Vt,42:$Vu,48:51,49:50},o($Vs,[2,48]),{5:54,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},o($Vs,[2,3]),o($Vs,[2,4]),{19:[1,55],25:[2,37]},{15:[1,56]},{60:$Vv,62:57,63:58,64:$Vw,65:$Vx,66:$Vy,67:$Vz},{1:[2,2]},{5:64,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},{5:65,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},{21:[1,66]},o($VA,[2,22]),o($VA,[2,23]),o($VA,[2,24]),o($VA,[2,25]),o($VA,[2,26]),o($VA,[2,27]),o($VA,[2,28]),o($VA,[2,29]),o($VA,[2,30]),o($VA,[2,31]),o($VA,[2,32]),o($VA,[2,33]),o($VA,[2,34]),o($Vs,[2,11]),o($Vs,[2,12]),{18:67,58:$Vb},{20:27,21:$Vd,22:$Ve,26:[1,68],27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr},o($VB,[2,39],{9:4,12:5,18:9,23:10,24:11,6:13,45:16,51:19,7:21,46:69,5:70,8:$V0,10:$V1,11:$V2,13:$V3,14:$V4,15:$V5,16:$V6,25:$V7,42:$V8,50:$V9,52:$Va,58:$Vb,61:$Vc}),{58:[1,71],60:[1,72]},o($VC,[2,55]),o($Vs,[2,47],{48:73,19:$Vt,42:$Vu}),o($VD,[2,45]),{15:[1,74]},{8:[1,75],33:[1,76]},{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr,47:[1,77]},{15:[1,78]},{14:[1,79]},{60:$Vv,61:[1,80],63:81,64:$Vw,65:$Vx,66:$Vy,67:$Vz},o($VE,[2,58]),o($VE,[2,60]),o($VE,[2,61]),o($VE,[2,62]),o($VE,[2,63]),o($VE,[2,64]),o($VF,[2,16],{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr}),o($VF,[2,17],{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr}),{5:82,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},o($Vs,[2,14],{19:[1,83]}),o($Vs,[2,21]),{26:[1,84],47:[1,85]},o($VB,[2,40],{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr}),o([4,19,21,22,26,27,28,29,30,31,32,33,34,35,36,37,38,39,44,47,53],[2,54]),o($VC,[2,56]),o($VD,[2,46]),o($VD,[2,42]),{44:[1,86]},{44:[1,87]},{5:88,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},{25:[2,38]},{5:89,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},o($Vs,[2,57]),o($VE,[2,59]),o($VF,[2,18],{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr}),{18:90,58:$Vb},o($Vs,[2,36]),{5:91,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},o($VD,[2,43]),o($VD,[2,44]),{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr,44:[1,92]},{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr,47:[1,93]},o($Vs,[2,15]),o($VB,[2,41],{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr}),o($Vs,[2,49]),{15:[1,94]},{14:[1,95]},{5:96,6:13,7:21,8:$V0,9:4,10:$V1,11:$V2,12:5,13:$V3,14:$V4,15:$V5,16:$V6,18:9,23:10,24:11,25:$V7,42:$V8,45:16,50:$V9,51:19,52:$Va,58:$Vb,61:$Vc},{20:27,21:$Vd,22:$Ve,27:$Vf,28:$Vg,29:$Vh,30:$Vi,31:$Vj,32:$Vk,33:$Vl,34:$Vm,35:$Vn,36:$Vo,37:$Vp,38:$Vq,39:$Vr,53:[1,97]},o($Vs,[2,50])],
defaultActions: {2:[2,1],26:[2,2],78:[2,38]},
table: [{3:1,4:[1,2],5:3,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{1:[3]},{1:[2,1]},{4:[1,27],22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu},o($Vv,[2,9]),o($Vv,[2,10]),o($Vv,[2,11]),{16:[1,45]},{5:46,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{19:[1,47]},o($Vv,[2,15],{21:[1,48]}),o($Vv,[2,25]),o($Vv,[2,26]),{5:49,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},o($Vv,[2,5]),o($Vv,[2,6]),o($Vv,[2,7]),o($Vv,[2,8]),{29:[1,50]},{65:51,66:[1,52]},{21:$Vw,46:$Vx,52:54,53:53},{16:$Vy,57:57,59:58},{5:61,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,55:60,56:$Vc,64:$Vd,67:$Ve},o($Vv,[2,3]),o($Vv,[2,4]),{21:[1,62],29:[2,43]},{66:$Vz,67:[1,63],68:64,69:65,70:$VA,71:$VB,72:$VC,73:$VD},{1:[2,2]},{5:71,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{5:72,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{23:[1,73],25:[1,74]},{5:76,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,26:75,27:11,28:12,29:$VE,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},o($VF,[2,28]),o($VF,[2,29]),o($VF,[2,30]),o($VF,[2,31]),o($VF,[2,32]),o($VF,[2,33]),o($VF,[2,34]),o($VF,[2,35]),o($VF,[2,36]),o($VF,[2,37]),o($VF,[2,38]),o($VF,[2,39]),o($VF,[2,40]),o($Vv,[2,12]),o([4,30,31,32,33,34,35,36,37,38,39,48,51,58],[2,13],{22:28,23:$Vf,24:$Vg,25:$Vh,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o($Vv,[2,14]),{20:78,64:$Vd},{22:28,23:$Vf,24:$Vg,25:$Vh,30:$VG,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu},o($VH,[2,45],{9:4,13:5,20:10,27:11,28:12,6:14,49:18,7:23,50:80,5:81,8:$V0,10:$V1,11:$V2,12:$V3,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,29:$V9,46:$Va,54:$Vb,56:$Vc,64:$Vd,67:$Ve}),{64:[1,82],66:[1,83]},o($VI,[2,66]),o($Vv,[2,53],{52:84,21:$Vw,46:$Vx}),o($VJ,[2,51]),{16:[1,85]},{8:[1,86],37:[1,87]},{51:[1,89],58:[1,88]},o($VK,[2,59]),{15:[1,90]},{48:[1,91],51:$VL},o([48,51],$VM,{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),{16:[1,93]},o($Vv,[2,68]),{66:$Vz,67:[1,94],69:95,70:$VA,71:$VB,72:$VC,73:$VD},o($VN,[2,70]),o($VN,[2,72]),o($VN,[2,73]),o($VN,[2,74]),o($VN,[2,75]),o($VN,[2,76]),o($VO,[2,18],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o($VO,[2,19],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),{5:96,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{5:98,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,26:97,27:11,28:12,29:$VE,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},o($Vv,[2,21]),o($VO,[2,23],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),{5:100,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,55:99,56:$Vc,64:$Vd,67:$Ve},o($Vv,[2,16],{21:[1,101]}),o($Vv,[2,27]),{30:[1,102],51:[1,103]},o($VH,[2,46],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o([4,21,23,24,25,30,31,32,33,34,35,36,37,38,39,40,41,42,43,48,51,58],[2,65]),o($VI,[2,67]),o($VJ,[2,52]),o($VJ,[2,48]),{48:[1,104]},{48:[1,105]},o($Vv,[2,55]),{16:$Vy,59:106},{5:107,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},o($Vv,[2,56]),{5:108,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},{29:[2,44]},o($Vv,[2,69]),o($VN,[2,71]),o($VO,[2,20],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o($Vv,[2,22]),o($VO,[2,24],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),{30:[1,109],51:$VL},{22:28,23:$Vf,24:$Vg,25:$Vh,30:$VG,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu,51:$VM},{20:110,64:$Vd},o($Vv,[2,42]),{5:111,6:14,7:23,8:$V0,9:4,10:$V1,11:$V2,12:$V3,13:5,14:$V4,15:$V5,16:$V6,17:$V7,18:$V8,20:10,27:11,28:12,29:$V9,46:$Va,49:18,54:$Vb,56:$Vc,64:$Vd,67:$Ve},o($VJ,[2,49]),o($VJ,[2,50]),o($VK,[2,60]),o($VK,[2,61],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o([30,48,51],[2,58],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu}),o($Vv,[2,54]),o($Vv,[2,17]),o($VH,[2,47],{22:28,23:$Vf,24:$Vg,25:$Vh,31:$Vi,32:$Vj,33:$Vk,34:$Vl,35:$Vm,36:$Vn,37:$Vo,38:$Vp,39:$Vq,40:$Vr,41:$Vs,42:$Vt,43:$Vu})],
defaultActions: {2:[2,1],27:[2,2],93:[2,44]},
parseError: function parseError(str, hash) {

@@ -275,3 +345,9 @@ if (hash.recoverable) {

} else {
throw new Error(str);
function _parseError (msg, hash) {
this.message = msg;
this.hash = hash;
}
_parseError.prototype = Error;
throw new _parseError(str, hash);
}

@@ -309,3 +385,3 @@ },

_token_stack:
function lex() {
var lex = function () {
var token;

@@ -317,3 +393,3 @@ token = lexer.lex() || EOF;

return token;
}
};
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;

@@ -839,69 +915,83 @@ while (true) {

break;
case 1:return 25;
case 1:return 29;
break;
case 2:return 26;
case 2:return 30;
break;
case 3:return 8;
break;
case 4:return 10;
case 4:return 8;
break;
case 5:return 11;
case 5:return 10;
break;
case 6:return 21;
case 6:return 11;
break;
case 7:return 21;
case 7:return 23;
break;
case 8:return 22;
case 8:return 23;
break;
case 9:return 22;
case 9:return 24;
break;
case 10:return 13;
case 10:return 24;
break;
case 11:return 47;
case 11:return 25;
break;
case 12:return 27;
case 12:return 25;
break;
case 13:return 28;
case 13:return 12;
break;
case 14:return 29;
case 14:return 12;
break;
case 15:return 31;
case 15:return 14;
break;
case 16:return 32;
case 16:return 51;
break;
case 17:return 33;
case 17:return 31;
break;
case 18:return 34;
case 18:return 32;
break;
case 19:return 35;
case 19:return 33;
break;
case 20:return 29;
case 20:return 35;
break;
case 21:return 30;
case 21:return 36;
break;
case 22:return '!';
case 22:return 37;
break;
case 23:return 50;
case 23:return 38;
break;
case 24:return 19;
case 24:return 39;
break;
case 25:return 42;
case 25:return 33;
break;
case 26:return 44;
case 26:return 34;
break;
case 27:return 13;
case 27:return 17;
break;
case 28:return 14;
case 28:return 54;
break;
case 29:return 36;
case 29:return 21;
break;
case 30:return 37;
case 30:return 46;
break;
case 31:return 38;
case 31:return 48;
break;
case 32:return 39;
case 32:return 56;
break;
case 33:return 15;
case 33:return 58;
break;
case 34:
case 34:return 14;
break;
case 35:return 15;
break;
case 36:return 40;
break;
case 37:return 41;
break;
case 38:return 42;
break;
case 39:return 43;
break;
case 40:return 16;
break;
case 41:
if (yy_.yytext.match(/\r|\n/) && parser.restricted) {

@@ -914,3 +1004,3 @@ parser.restricted = false;

break;
case 35:
case 42:
if (yy_.yytext.match(/\r|\n/) && parser.restricted) {

@@ -923,32 +1013,32 @@ parser.restricted = false;

break;
case 36:this.begin('string_quoted_content'); parser.charUnescapeCurrentQuote = this.match; return 61;
case 43:this.begin('string_quoted_content'); parser.charUnescapeCurrentQuote = this.match; return 67;
break;
case 37:return 60;
case 44:return 66;
break;
case 38:return 64;
case 45:return 70;
break;
case 39:return 65;
case 46:return 71;
break;
case 40:return 66;
case 47:return 72;
break;
case 41:if (parser.charUnescapeCurrentQuote === this.match) { this.popState(); return 61; } else { return 60; }
case 48:if (parser.charUnescapeCurrentQuote === this.match) { this.popState(); return 67; } else { return 66; }
break;
case 42:return 60;
case 49:return 66;
break;
case 43:this.begin('backtick'); return 58;
case 50:this.begin('backtick'); return 64;
break;
case 44:return 60;
case 51:return 66;
break;
case 45:return 60;
case 52:return 66;
break;
case 46:this.popState(); return 58;
case 53:this.popState(); return 64;
break;
case 47:/* skip whitespaces */
case 54:/* skip whitespaces */
break;
case 48:return 'INVALID'
case 55:return 'INVALID'
break;
}
},
rules: [/^(?:$)/,/^(?:\()/,/^(?:\))/,/^(?:(([1-9][0-9]*)))/,/^(?:true\b)/,/^(?:false\b)/,/^(?:like\b)/,/^(?:LIKE\b)/,/^(?:not\b)/,/^(?:NOT\b)/,/^(?:\?)/,/^(?:,)/,/^(?:\|\|)/,/^(?:&&)/,/^(?:==)/,/^(?:\+)/,/^(?:-)/,/^(?:\*)/,/^(?:\/)/,/^(?:%)/,/^(?:==)/,/^(?:!=)/,/^(?:!)/,/^(?:\$)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\?)/,/^(?::)/,/^(?:<)/,/^(?:>)/,/^(?:<=)/,/^(?:>=)/,/^(?:(([A-Za-z_][A-Za-z_0-9]*)))/,/^(?:\/\*(.|\r|\n)*?\*\/)/,/^(?:\/\/.*($|\r|\n))/,/^(?:((['"])))/,/^(?:\s+)/,/^(?:((\\[Xx][A-Fa-f0-9]{1,2})))/,/^(?:((\\?[0-7]{1,3})))/,/^(?:((\\[abfnrtv\\/'"])))/,/^(?:((['"])))/,/^(?:(([^\0\n])))/,/^(?:`)/,/^(?:\s+)/,/^(?:[^`])/,/^(?:`)/,/^(?:\s+)/,/^(?:.)/],
conditions: {"backtick":{"rules":[44,45,46],"inclusive":false},"string_quoted_content":{"rules":[37,38,39,40,41,42],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,43,47,48],"inclusive":true}}
rules: [/^(?:$)/,/^(?:\()/,/^(?:\))/,/^(?:0\b)/,/^(?:(([1-9][0-9]*)))/,/^(?:true\b)/,/^(?:false\b)/,/^(?:like\b)/,/^(?:LIKE\b)/,/^(?:not\b)/,/^(?:NOT\b)/,/^(?:in\b)/,/^(?:IN\b)/,/^(?:NULL\b)/,/^(?:null\b)/,/^(?:\?)/,/^(?:,)/,/^(?:\|\|)/,/^(?:&&)/,/^(?:==)/,/^(?:\+)/,/^(?:-)/,/^(?:\*)/,/^(?:\/)/,/^(?:%)/,/^(?:==)/,/^(?:!=)/,/^(?:!)/,/^(?:\$)/,/^(?:\.)/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:\?)/,/^(?::)/,/^(?:<)/,/^(?:>)/,/^(?:<=)/,/^(?:>=)/,/^(?:(([A-Za-z_][A-Za-z_0-9]*)))/,/^(?:\/\*(.|\r|\n)*?\*\/)/,/^(?:\/\/.*($|\r|\n))/,/^(?:((['"])))/,/^(?:\s+)/,/^(?:((\\[Xx][A-Fa-f0-9]{1,2})))/,/^(?:((\\?[0-7]{1,3})))/,/^(?:((\\[abfnrtv\\/'"])))/,/^(?:((['"])))/,/^(?:(([^\0\n])))/,/^(?:`)/,/^(?:\s+)/,/^(?:[^`])/,/^(?:`)/,/^(?:\s+)/,/^(?:.)/],
conditions: {"backtick":{"rules":[51,52,53],"inclusive":false},"string_quoted_content":{"rules":[44,45,46,47,48,49],"inclusive":false},"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,50,54,55],"inclusive":true}}
});

@@ -955,0 +1045,0 @@ return lexer;

@@ -76,8 +76,2 @@ /*

Client.joinOperatios = {
"LEFT": Messages.messages['Mysqlx.Crud.Join'].enums.Operation.LEFT,
"RIGHT": Messages.messages['Mysqlx.Crud.Join'].enums.Operation.RIGHT,
"INNER": Messages.messages['Mysqlx.Crud.Join'].enums.Operation.INNER
};
// TODO - This is a hack, see also TODO in ResponseHandler.prototype.sendMessage

@@ -309,8 +303,7 @@ Client.serverGoneMessageId = -1;

Client.prototype.crudInsert = function (schema, collection, model, rows, projection) {
if (!rows.length) {
return Promise.reject(new Error('No document provided for Crud::Insert'));
}
Client.prototype.crudInsert = function (schema, collection, model, data, options) {
data = Object.assign({}, { columns: [], rows: [] }, data);
options = Object.assign({}, { upsert: false }, options);
const data = {
const message = {
collection: {

@@ -321,6 +314,7 @@ schema: schema,

data_model: model,
projection: projection || []
projection: data.columns,
upsert: options.upsert
};
data.row = rows.map(row => ({
message.row = data.rows.map(row => ({
field: row.map(field => ({

@@ -332,9 +326,9 @@ type: Messages.messages['Mysqlx.Expr.Expr'].enums.Type.LITERAL,

const buffer = this.encodeMessage(Messages.ClientMessages.CRUD_INSERT, data);
const protobuf = this.encodeMessage(Messages.ClientMessages.CRUD_INSERT, message);
const handler = new handlers.SqlResultHandler();
return handler.sendMessage(this._workQueue, this._stream, buffer);
return handler.sendMessage(this._workQueue, this._stream, protobuf);
};
Client.prototype.crudFind = function (session, schema, collection, model, projection, criteria, group, having, order, limit, rowcb, metacb, bounds, joins) {
Client.prototype.crudFind = function (session, schema, collection, model, projection, criteria, group, having, order, limit, rowcb, metacb, bounds, joins, locking) {
const data = {

@@ -404,2 +398,6 @@ collection: {

if (locking) {
data.locking = locking;
}
if (criteria && criteria.placeholders && criteria.placeholders.named.length) {

@@ -411,2 +409,3 @@ data.args = criteria.placeholders.named.map(key => DataType.encodeScalar(bounds[key]));

handler = new handlers.SqlResultHandler(rowcb, metacb);
return handler.sendMessage(this._workQueue, this._stream, buffer);

@@ -413,0 +412,0 @@ };

@@ -13,2 +13,15 @@ {

"TABLE": 2
},
"ViewAlgorithm": {
"UNDEFINED": 1,
"MERGE": 2,
"TEMPTABLE": 3
},
"ViewSqlSecurity": {
"INVOKER": 1,
"DEFINER": 2
},
"ViewCheckOption": {
"LOCAL": 1,
"CASCADED": 2
}

@@ -61,7 +74,2 @@ },

"tag": 2
},
"alias": {
"rule": "optional",
"type": "string",
"tag": 3
}

@@ -137,8 +145,7 @@ }

},
"Join": {
"Find": {
"enums": {
"Operation": {
"LEFT": 0,
"RIGHT": 1,
"INNER": 2
"RowLock": {
"SHARED_LOCK": 1,
"EXCLUSIVE_LOCK": 2
}

@@ -150,26 +157,4 @@ },

"type": "Collection",
"tag": 1
},
"match": {
"rule": "required",
"type": "Mysqlx.Expr.Expr",
"tag": 2
},
"operation": {
"rule": "optional",
"type": "Operation",
"tag": 3,
"options": {
"default": "LEFT"
}
}
}
},
"Find": {
"fields": {
"collection": {
"rule": "required",
"type": "Collection",
"tag": 2
},
"data_model": {

@@ -215,6 +200,6 @@ "rule": "optional",

},
"join": {
"rule": "repeated",
"type": "Join",
"tag": 10
"locking": {
"rule": "optional",
"type": "RowLock",
"tag": 12
}

@@ -260,2 +245,10 @@ }

"tag": 5
},
"upsert": {
"rule": "optional",
"type": "bool",
"tag": 6,
"options": {
"default": false
}
}

@@ -336,2 +329,111 @@ }

}
},
"CreateView": {
"fields": {
"collection": {
"rule": "required",
"type": "Collection",
"tag": 1
},
"definer": {
"rule": "optional",
"type": "string",
"tag": 2
},
"algorithm": {
"rule": "optional",
"type": "ViewAlgorithm",
"tag": 3,
"options": {
"default": "UNDEFINED"
}
},
"security": {
"rule": "optional",
"type": "ViewSqlSecurity",
"tag": 4,
"options": {
"default": "DEFINER"
}
},
"check": {
"rule": "optional",
"type": "ViewCheckOption",
"tag": 5
},
"column": {
"rule": "repeated",
"type": "string",
"tag": 6
},
"stmt": {
"rule": "required",
"type": "Find",
"tag": 7
},
"replace_existing": {
"rule": "optional",
"type": "bool",
"tag": 8,
"options": {
"default": false
}
}
}
},
"ModifyView": {
"fields": {
"collection": {
"rule": "required",
"type": "Collection",
"tag": 1
},
"definer": {
"rule": "optional",
"type": "string",
"tag": 2
},
"algorithm": {
"rule": "optional",
"type": "ViewAlgorithm",
"tag": 3
},
"security": {
"rule": "optional",
"type": "ViewSqlSecurity",
"tag": 4
},
"check": {
"rule": "optional",
"type": "ViewCheckOption",
"tag": 5
},
"column": {
"rule": "repeated",
"type": "string",
"tag": 6
},
"stmt": {
"rule": "optional",
"type": "Find",
"tag": 7
}
}
},
"DropView": {
"fields": {
"collection": {
"rule": "required",
"type": "Collection",
"tag": 1
},
"if_exists": {
"rule": "optional",
"type": "bool",
"tag": 2,
"options": {
"default": false
}
}
}
}

@@ -338,0 +440,0 @@ },

@@ -175,3 +175,3 @@ {

"rule": "required",
"type": "Expr",
"type": "Mysqlx.Expr.Expr",
"tag": 2

@@ -178,0 +178,0 @@ }

{
"imports": [
"mysqlx_sql.proto",
"mysqlx_resultset.proto",
"mysqlx_crud.proto",
"mysqlx_session.proto",
"mysqlx_connection.proto",
"mysqlx_expect.proto",
"mysqlx_notice.proto"
],
"options": {

@@ -31,3 +22,6 @@ "java_package": "com.mysql.cj.mysqlx.protobuf"

"EXPECT_OPEN": 24,
"EXPECT_CLOSE": 25
"EXPECT_CLOSE": 25,
"CRUD_CREATE_VIEW": 30,
"CRUD_MODIFY_VIEW": 31,
"CRUD_DROP_VIEW": 32
}

@@ -34,0 +28,0 @@ },

{
"name": "@mysql/xdevapi",
"version": "8.0.7",
"version": "8.0.8",
"description": "MySQL Connector/Node.JS - A Node.JS driver for MySQL using the X Protocol and X DevAPI.",
"author": "Johannes Schlüter <johannes.schlueter@oracle.com>",
"contributors": [{
"name": "Rui Quelhas",
"email": "rui.quelhas@oracle.com"
}],
"contributors": [
{
"name": "Rui Quelhas",
"email": "rui.quelhas@oracle.com"
}
],
"license": "GPL-2.0",

@@ -11,0 +13,0 @@ "files": [

@@ -24,3 +24,3 @@ # MySQL Connector/Node.js with X DevAPI

```sh
$ npm install mysql-connector-nodejs-8.0.7.tar.gz`
$ npm install mysql-connector-nodejs-8.0.8.tar.gz`
```

@@ -27,0 +27,0 @@ * use the @mysql/xdevapi package from [https://npmjs.com](https://npmjs.com) and install it:

@@ -12,28 +12,24 @@ 'use strict';

exports.setup = function () {
let session;
return mysqlx
.getSession(config)
.then(s => {
session = s;
return session.dropSchema(config.schema);
.then(session => {
return session.dropSchema(config.schema).then(() => session);
})
.then(() => {
return session.createSchema(config.schema);
})
.then(schema => {
return { session, schema };
.then(session => {
return session.createSchema(config.schema).then(schema => ({ session, schema }));
});
};
exports.teardown = function () {
return mysqlx
.getSession(config)
.then(session => {
return session.dropSchema(config.schema).then(() => session);
})
.then(session => {
exports.teardown = function (session, schema) {
if (!session) {
return Promise.reject(new Error(`cannot close \`${session}\` session`));
}
const schemaName = !schema ? config.schema : schema.getName();
return session
.dropSchema(schemaName)
.then(() => {
return session.close();
});
};

@@ -5,7 +5,12 @@ 'use strict';

const expect = require('chai').expect;
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const fixtures = require('test/fixtures');
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('@integration document collection add', () => {
let collection, schema;
let session, schema, collection;

@@ -15,2 +20,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -29,3 +35,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -32,0 +38,0 @@

@@ -9,3 +9,3 @@ 'use strict';

describe('@integration document collection find', () => {
let schema, collection;
let session, schema, collection;

@@ -15,2 +15,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -29,3 +30,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -121,2 +122,55 @@

});
context('single document retrieval', () => {
beforeEach('add fixtures', () => {
return collection
.add({ _id: '1', name: 'foo' })
.add({ _id: '2', name: 'bar' })
.execute();
});
it('should return an existing document with the given id', () => {
const expected = { _id: '1', name: 'foo' };
return collection
.getOne('1')
.then(doc => expect(doc).to.deep.equal(expected));
});
it('should return null if a document with the given id does not exist', () => {
return collection
.getOne('3')
.then(doc => expect(doc).to.be.null);
});
});
context('multi-option expressions', () => {
beforeEach('add fixtures', () => {
return collection
.add({ _id: '1', name: 'foo' })
.add({ _id: '2', name: 'bar' })
.add({ _id: '3', name: 'baz' })
.execute();
});
it('should return all documents that match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '1', name: 'foo' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.find("$._id in ('1', '3')")
.execute(doc => doc && actual.push(doc))
.then(() => expect(actual).to.deep.equal(expected));
});
it('should return all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '2', name: 'bar' }];
let actual = [];
return collection
.find("$._id not in ('1', '3')")
.execute(doc => doc && actual.push(doc))
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -29,5 +29,5 @@ 'use strict';

return collection
.add({ _id: 1, name: 'foo' })
.add({ _id: 2, name: 'bar' })
.add({ _id: 3, name: 'baz' })
.add({ _id: '1', name: 'foo' })
.add({ _id: '2', name: 'bar' })
.add({ _id: '3', name: 'baz' })
.execute();

@@ -37,3 +37,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown(session);
return fixtures.teardown(session, schema);
});

@@ -43,3 +43,3 @@

it('should updated all documents in a collection', () => {
const expected = [{ _id: 1, name: 'qux' }, { _id: 2, name: 'qux' }, { _id: 3, name: 'qux' }];
const expected = [{ _id: '1', name: 'qux' }, { _id: '2', name: 'qux' }, { _id: '3', name: 'qux' }];
let actual = [];

@@ -58,3 +58,3 @@

it('should update the documents from a collection that match the criteria', () => {
const expected = [{ _id: 1, name: 'foo' }, { _id: 2, name: 'qux' }, { _id: 3, name: 'baz' }];
const expected = [{ _id: '1', name: 'foo' }, { _id: '2', name: 'qux' }, { _id: '3', name: 'baz' }];
let actual = [];

@@ -73,3 +73,3 @@

it('should modify a given number of documents', () => {
const expected = [{ _id: 1, name: 'qux' }, { _id: 2, name: 'bar' }, { _id: 3, name: 'baz' }];
const expected = [{ _id: '1', name: 'qux' }, { _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];

@@ -86,2 +86,70 @@

});
context('single document replacement', () => {
it('should replace the entire document if it exists', () => {
const expected = [{ _id: '1', age: 23 }, { _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.replaceOne('1', { _id: '3', age: 23 })
.then(result => {
expect(result.getAffectedItemsCount()).to.equal(1);
return collection
.find()
.execute(doc => actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should do nothing if the document does not exist', () => {
const expected = [{ _id: '1', name: 'foo' }, { _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.replaceOne('4', { _id: '1', name: 'baz', age: 23 })
.then(result => {
expect(result.getAffectedItemsCount()).to.equal(0);
return collection
.find()
.execute(doc => actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
context('multi-option expressions', () => {
it('should modify all documents that match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '1', name: 'qux' }, { _id: '2', name: 'bar' }, { _id: '3', name: 'qux' }];
let actual = [];
return collection
.modify("$._id in ('1', '3')")
.set('$.name', 'qux')
.execute()
.then(() => {
return collection
.find()
.execute(doc => doc && actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should modify all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '1', name: 'foo' }, { _id: '2', name: 'qux' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.modify("$._id not in ('1', '3')")
.set('$.name', 'qux')
.execute()
.then(() => {
return collection
.find()
.execute(doc => doc && actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -29,5 +29,5 @@ 'use strict';

return collection
.add({ _id: 1, name: 'foo' })
.add({ _id: 2, name: 'bar' })
.add({ _id: 3, name: 'baz' })
.add({ _id: '1', name: 'foo' })
.add({ _id: '2', name: 'bar' })
.add({ _id: '3', name: 'baz' })
.execute();

@@ -37,3 +37,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown(session);
return fixtures.teardown(session, schema);
});

@@ -61,3 +61,3 @@

it('should remove the documents from a collection that match the criteria', () => {
const expected = [{ _id: 2, name: 'bar' }, { _id: 3, name: 'baz' }];
const expected = [{ _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];

@@ -75,3 +75,3 @@

it('should remove a given number of documents', () => {
const expected = [{ _id: 3, name: 'baz' }];
const expected = [{ _id: '3', name: 'baz' }];
let actual = [];

@@ -87,2 +87,68 @@

});
context('single document removal', () => {
it('should remove an existing document with the given id', () => {
const expected = [{ _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.removeOne('1')
.then(result => {
expect(result.getAffectedItemsCount()).to.equal(1);
return collection
.find()
.execute(doc => actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should do nothing if no document exists with the given id', () => {
const expected = [{ _id: '1', name: 'foo' }, { _id: '2', name: 'bar' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.removeOne('4')
.then(result => {
expect(result.getAffectedItemsCount()).to.equal(0);
return collection
.find()
.execute(doc => actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
context('multi-option expressions', () => {
it('should remove all documents that match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '2', name: 'bar' }];
let actual = [];
return collection
.remove("$._id in ('1', '3')")
.execute()
.then(() => {
return collection
.find()
.execute(doc => doc && actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should remove all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [{ _id: '1', name: 'foo' }, { _id: '3', name: 'baz' }];
let actual = [];
return collection
.remove("$._id not in ('1', '3')")
.execute()
.then(() => {
return collection
.find()
.execute(doc => doc && actual.push(doc));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -10,3 +10,3 @@ 'use strict';

describe('@integration collection miscellaneous tests', () => {
let schema, collection;
let session, schema, collection;

@@ -16,2 +16,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -28,3 +29,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -69,3 +70,3 @@

]).then(() => {
collection.find().execute(actual => expect(actual).to.deep.equal(expected));
return collection.find().execute(actual => expect(actual).to.deep.equal(expected));
});

@@ -89,3 +90,3 @@ });

]).then(() => {
collection.find().execute(actual => expect(actual).to.be.empty);
return collection.find().execute(actual => expect(actual).to.be.undefined);
});

@@ -102,3 +103,3 @@ });

]).then(() => {
collection.find().execute(actual => expect(actual).to.deep.equal(document2));
return collection.find().execute(actual => expect(actual).to.deep.equal(document2));
});

@@ -105,0 +106,0 @@ });

@@ -11,3 +11,3 @@ 'use strict';

describe('@integration relational miscellaneous tests', () => {
let session;
let session, schema;

@@ -18,2 +18,3 @@ beforeEach('set context', () => {

session = suite.session;
schema = suite.schema;
});

@@ -23,3 +24,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -26,0 +27,0 @@

@@ -9,3 +9,3 @@ 'use strict';

describe('@integration relational table delete', () => {
let schema, table;
let session, schema, table;

@@ -15,2 +15,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -33,3 +34,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -120,2 +121,36 @@

});
context('multi-option expressions', () => {
it('should return all documents that match a criteria specified by a grouped expression', () => {
const expected = [];
let actual = [];
return table
.delete()
.where("`name` in ('foo', 'bar', 'baz')")
.execute()
.then(() => {
return table
.select()
.execute(row => row && row.length && actual.push(row));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should return all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [['foo', 42], ['bar', 23], ['baz', 42]];
let actual = [];
return table
.delete()
.where('`age` not in (23, 42)')
.execute()
.then(() => {
return table
.select()
.execute(row => row && row.length && actual.push(row));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -9,3 +9,3 @@ 'use strict';

describe('@integration relational table insert', () => {
let schema, table;
let session, schema, table;

@@ -15,2 +15,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -33,3 +34,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -36,0 +37,0 @@

@@ -9,3 +9,3 @@ 'use strict';

describe('@integration relational table select', () => {
let schema;
let session, schema;

@@ -15,2 +15,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -35,3 +36,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -245,2 +246,38 @@

});
context('multi-option expressions', () => {
beforeEach('add fixtures', () => {
return schema
.getTable('test')
.insert(['test2', 'test3'])
.values(['foo', 42])
.values(['bar', 23])
.values(['baz', 42])
.execute();
});
it('should return all documents that match a criteria specified by a grouped expression', () => {
const expected = [[1, 'foo', 42], [3, 'baz', 42]];
let actual = [];
return schema
.getTable('test')
.select()
.where("`test2` in ('foo', 'baz')")
.execute(row => row && row.length && actual.push(row))
.then(() => expect(actual).to.deep.equal(expected));
});
it('should return all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [[2, 'bar', 23]];
let actual = [];
return schema
.getTable('test')
.select()
.where('`test3` not in (50, 42)')
.execute(row => row && row.length && actual.push(row))
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -9,3 +9,3 @@ 'use strict';

describe('@integration relational table update', () => {
let schema, table;
let session, schema, table;

@@ -15,2 +15,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -43,3 +44,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -128,2 +129,38 @@

});
context('multi-option expressions', () => {
it('should update all documents that match a criteria specified by a grouped expression', () => {
const expected = [['foo', 50], ['bar', 50], ['baz', 42]];
let actual = [];
return table
.update()
.where("`name` in ('foo', 'bar')")
.set('age', 50)
.execute()
.then(() => {
return table
.select()
.execute(row => row && row.length && actual.push(row));
})
.then(() => expect(actual).to.deep.equal(expected));
});
it('should update all documents that do not match a criteria specified by a grouped expression', () => {
const expected = [['foo', 42], ['qux', 23], ['baz', 42]];
let actual = [];
return table
.update()
.where('`age` not in (42, 50)')
.set('name', 'qux')
.execute()
.then(() => {
return table
.select()
.execute(row => row && row.length && actual.push(row));
})
.then(() => expect(actual).to.deep.equal(expected));
});
});
});

@@ -14,3 +14,3 @@ 'use strict';

describe('@integration session schema', () => {
let schema;
let session, schema;

@@ -20,2 +20,3 @@ beforeEach('set context', () => {

// TODO(rui.quelhas): use ES6 destructuring assignment for node >=6.0.0
session = suite.session;
schema = suite.schema;

@@ -26,3 +27,3 @@ });

afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session, schema);
});

@@ -32,6 +33,2 @@

const collections = ['test1', 'test2'];
const expected = {
[collections[0]]: schema.getCollection(collections[0]),
[collections[1]]: schema.getCollection(collections[1])
};

@@ -41,4 +38,9 @@ return expect(Promise.all([

schema.createCollection(collections[1]),
expect(schema.getCollections()).to.eventually.deep.equal(expected)
])).to.be.fulfilled;
schema.getCollections()
])).to.be.fulfilled.then(result => {
expect(Object.keys(result[2])).to.have.lengthOf(2);
expect(result[2]).to.have.keys(collections);
expect(result[2][collections[0]].inspect()).to.deep.equal(result[0].inspect());
expect(result[2][collections[1]].inspect()).to.deep.equal(result[1].inspect());
});
});

@@ -45,0 +47,0 @@

@@ -9,4 +9,4 @@ 'use strict';

const config = require('test/properties');
const fs = require('fs');
const fixtures = require('test/fixtures');
const fs = require('fs');
const mysqlx = require('index');

@@ -23,3 +23,3 @@ const os = require('os');

it('should connect to the server in a new session', () => {
const tcpConfig = Object.assign({}, config);
const tcpConfig = Object.assign({}, config, { socket: undefined });

@@ -31,3 +31,3 @@ return expect(mysqlx.getSession(tcpConfig)).to.be.fulfilled

it('should connect to the server with an IPv6 host', () => {
const ipv6Config = Object.assign({}, config, { host: '::1' });
const ipv6Config = Object.assign({}, config, { host: '::1', socket: undefined });

@@ -51,3 +51,3 @@ return expect(mysqlx.getSession(ipv6Config)).to.be.fulfilled

it('should connect to the server insecurely if SSL/TLS is disabled explicitly', () => {
const insecureConfig = Object.assign({}, config, { ssl: false });
const insecureConfig = Object.assign({}, config, { ssl: false, socket: undefined });

@@ -82,3 +82,3 @@ return expect(mysqlx.getSession(insecureConfig)).to.be.fulfilled

expect(session).to.be.an.instanceof(Session);
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -174,8 +174,8 @@ return session.close();

it('should connect to the server with a local UNIX socket', function () {
if (!config.socket) {
if (!config.socket || os.platform() === 'win32') {
return this.skip();
}
// Uses the default socket allocated for MySQL.
const uri = `mysqlx://${config.dbUser}:${config.dbPassword}@(${config.socket})`;
// Uses the default socket allocated for MySQL and a working authentication method.
const uri = `mysqlx://${config.dbUser}:${config.dbPassword}@(${config.socket})?auth=MYSQL41`;
const expected = { dbUser: config.dbUser, host: undefined, port: undefined, socket: config.socket, ssl: false };

@@ -185,3 +185,3 @@

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -198,3 +198,3 @@ return session.close();

// Uses the default socket allocated for MySQL.
const uri = `mysqlx://${config.dbUser}:${config.dbPassword}@(${config.socket})?ssl-mode=REQUIRED&ssl-ca=(/path/to/ca.pem)?ssl-crl=(/path/to/crl.pem)`;
const uri = `mysqlx://${config.dbUser}:${config.dbPassword}@(${config.socket})?ssl-mode=REQUIRED&ssl-ca=(/path/to/ca.pem)?ssl-crl=(/path/to/crl.pem)&auth=MYSQL41`;
const expected = { dbUser: config.dbUser, host: undefined, port: undefined, socket: config.socket, ssl: false };

@@ -204,3 +204,3 @@

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -311,3 +311,3 @@ return session.close();

it('should connect to the server with a local UNIX socket', function () {
if (!config.socket) {
if (!config.socket || os.platform() === 'win32') {
return this.skip();

@@ -317,3 +317,3 @@ }

// Uses the default socket allocated for MySQL.
const uri = `${config.dbUser}:${config.dbPassword}@(${config.socket})`;
const uri = `${config.dbUser}:${config.dbPassword}@(${config.socket})?auth=MYSQL41`;
const expected = { dbUser: config.dbUser, host: undefined, port: undefined, socket: config.socket, ssl: false };

@@ -323,3 +323,3 @@

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -336,3 +336,3 @@ return session.close();

// Uses the default socket allocated for MySQL.
const uri = `${config.dbUser}:${config.dbPassword}@(${config.socket})?ssl-mode=REQUIRED&ssl-ca=(/path/to/ca.pem)?ssl-crl=(/path/to/crl.pem)`;
const uri = `${config.dbUser}:${config.dbPassword}@(${config.socket})?ssl-mode=REQUIRED&ssl-ca=(/path/to/ca.pem)?ssl-crl=(/path/to/crl.pem)&auth=MYSQL41`;
const expected = { dbUser: config.dbUser, host: undefined, port: undefined, socket: config.socket, ssl: false };

@@ -342,3 +342,3 @@

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -371,3 +371,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -385,3 +385,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -397,3 +397,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -409,3 +409,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -424,3 +424,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -437,3 +437,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -450,3 +450,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -481,4 +481,4 @@ return session.close();

// TODO(Rui): maybe add some appdata to the object.
const data = Object.assign({}, config);
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: undefined, ssl: true };
const data = Object.assign({}, config, { socket: undefined });
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: data.socket, ssl: true };

@@ -488,3 +488,3 @@ return expect(mysqlx.config.save('test', data)).to.be.fulfilled

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -501,4 +501,4 @@ return session.close();

it('should save the session details the using name and a JSON config', () => {
const data = Object.assign({}, config, { ssl: false });
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: undefined, ssl: false };
const data = Object.assign({}, config, { socket: undefined, ssl: false });
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: data.socket, ssl: false };

@@ -508,3 +508,3 @@ return expect(mysqlx.config.save('test', JSON.stringify(data))).to.be.fulfilled

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -527,3 +527,3 @@ return session.close();

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -538,4 +538,4 @@ return session.close();

// TODO(Rui): maybe add some appdata to the object.
const data = Object.assign({}, config, { ssl: false });
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: undefined, ssl: false };
const data = Object.assign({}, config, { ssl: false, socket: undefined });
const expected = { dbUser: data.dbUser, host: data.host, port: data.port, socket: data.socket, ssl: false };

@@ -550,3 +550,3 @@ return expect(mysqlx.config.save('test', data)).to.be.fulfilled

.then(session => {
expect(session.inspect()).to.deep.equal(expected);
expect(session.inspect()).to.deep.include(expected);

@@ -594,32 +594,35 @@ return session.close();

context('database management', () => {
let session;
beforeEach('set context', () => {
return fixtures.setup().then(suite => {
session = suite.session;
});
});
afterEach('clear context', () => {
return fixtures.teardown();
return fixtures.teardown(session);
});
it('should allow to drop an existing schema', () => {
return mysqlx.getSession(config)
.then(session => expect(session.dropSchema(config.schema)).to.be.fulfilled);
return expect(session.dropSchema(config.schema)).to.be.fulfilled;
});
it('should not fail to drop a non-existent schema', () => {
return mysqlx.getSession(config)
.then(session => session.dropSchema(config.schema).then(() => session))
.then(session => expect(session.dropSchema(config.schema)).to.be.fulfilled);
return session.dropSchema(config.schema)
.then(() => expect(session.dropSchema(config.schema)).to.be.fulfilled);
});
it('should fail to drop a schema with an empty name', () => {
return mysqlx.getSession(config)
.then(session => expect(session.dropSchema('')).to.be.rejected);
return expect(session.dropSchema('')).to.be.rejected;
});
it('should fail to drop a schema with an invalid name', () => {
return mysqlx.getSession(config)
.then(session => expect(session.dropSchema(' ')).to.be.rejected);
return expect(session.dropSchema(' ')).to.be.rejected;
});
it('should fail to drop a schema with name set to `null`', () => {
return mysqlx.getSession(config)
.then(session => expect(session.dropSchema(null)).to.be.rejected);
return expect(session.dropSchema(null)).to.be.rejected;
});
});
});

@@ -20,3 +20,3 @@ 'use strict';

afterEach('clear context', () => {
return fixtures.teardown(session);
return fixtures.teardown(session, schema);
});

@@ -23,0 +23,0 @@

@@ -9,2 +9,3 @@ 'use strict';

const isRemoteServer = process.env.NODE_TEST_MYSQL_LOCATION === 'remote';
const isSecureServer = process.env.NODE_TEST_MYSQL_SSL !== 'false';

@@ -17,3 +18,4 @@ module.exports = {

schema: process.env.NODE_TEST_MYSQL_SCHEMA || 'nodejsmysqlxtest',
socket: !isRemoteServer && os.platform() !== 'win32' ? socket : undefined
socket: !isRemoteServer && os.platform() !== 'win32' ? socket : undefined,
ssl: isSecureServer
};

@@ -59,2 +59,9 @@ 'use strict';

});
it('should allow empty passwords', () => {
const username = 'foo';
const auth = new PlainAuth({ dbUser: username });
return expect(auth.getInitialAuthData()).to.not.throw;
});
});

@@ -6,9 +6,13 @@ 'use strict';

// npm `test` script was updated to use NODE_PATH=.
const Collection = require('lib/DevAPI/Collection');
const CollectionAdd = require('lib/DevAPI/CollectionAdd');
const CollectionModify = require('lib/DevAPI/CollectionModify');
const CollectionRemove = require('lib/DevAPI/CollectionRemove');
const expect = require('chai').expect;
const Client = require('lib/Protocol/Client');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const collection = require('lib/DevAPI/Collection');
const td = require('testdouble');
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('Collection', () => {

@@ -28,5 +32,3 @@ let sqlStmtExecute, getName;

it('should return the collection name', () => {
const collection = new Collection(null, null, 'foobar');
expect(collection.getName()).to.equal('foobar');
expect(collection(null, null, 'foobar').getName()).to.equal('foobar');
});

@@ -37,7 +39,7 @@ });

it('should return the associated schema', () => {
const collection = new Collection(null, { getName });
const instance = collection(null, { getName });
td.when(getName()).thenReturn('foobar');
expect(collection.getSchema().getName()).to.equal('foobar');
expect(instance.getSchema().getName()).to.equal('foobar');
});

@@ -48,5 +50,5 @@ });

it('should return the associated session', () => {
const collection = new Collection({ foo: 'bar' });
const instance = collection({ foo: 'bar' });
expect(collection.getSession()).to.deep.equal({ foo: 'bar' });
expect(instance.getSession()).to.deep.equal({ foo: 'bar' });
});

@@ -57,3 +59,3 @@ });

it('should return true if exists in database', () => {
const collection = new Collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');

@@ -63,7 +65,7 @@ td.when(getName()).thenReturn('bar');

return expect(collection.existsInDatabase()).to.eventually.be.true;
return expect(instance.existsInDatabase()).to.eventually.be.true;
});
it('should return false if it does not exist in database', () => {
const collection = new Collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');

@@ -73,3 +75,3 @@ td.when(getName()).thenReturn('bar');

return expect(collection.existsInDatabase()).to.eventually.be.false;
return expect(instance.existsInDatabase()).to.eventually.be.false;
});

@@ -80,3 +82,3 @@ });

it('should return the number of documents in a collection', () => {
const collection = new Collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');

@@ -86,7 +88,7 @@ td.when(getName()).thenReturn('bar');

return expect(collection.count()).to.eventually.equal(1);
return expect(instance.count()).to.eventually.equal(1);
});
it('should fail if an unexpected error is thrown', () => {
const collection = new Collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = collection({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const error = new Error('foobar');

@@ -97,3 +99,3 @@

return expect(collection.count()).to.eventually.be.rejectedWith(error);
return expect(instance.count()).to.eventually.be.rejectedWith(error);
});

@@ -104,3 +106,3 @@ });

it('should hide internals', () => {
const collection = new Collection(null, { getName }, 'foo');
const instance = collection(null, { getName }, 'foo');
const expected = { schema: 'bar', collection: 'foo' };

@@ -110,3 +112,3 @@

expect(collection.inspect()).to.deep.equal(expected);
expect(instance.inspect()).to.deep.equal(expected);
});

@@ -117,5 +119,5 @@ });

it('should return an instance of the proper class', () => {
const instance = (new Collection()).add({});
const instance = collection().add({});
expect(instance).to.be.an.instanceof(CollectionAdd);
expect(instance.getClassName()).to.equal('CollectionAdd');
});

@@ -125,5 +127,5 @@

const documents = [{ foo: 'bar' }, { foo: 'baz' }];
const instance = (new Collection()).add(documents);
const instance = collection().add(documents);
expect(instance._document).to.deep.equal(documents);
expect(instance.getDocuments()).to.deep.equal(documents);
});

@@ -133,5 +135,5 @@

const documents = [{ foo: 'bar' }, { foo: 'baz' }];
const instance = (new Collection()).add(documents[0], documents[1]);
const instance = collection().add(documents[0], documents[1]);
expect(instance._document).to.deep.equal(documents);
expect(instance.getDocuments()).to.deep.equal(documents);
});

@@ -144,11 +146,7 @@ });

const schema = 'bar';
const collection = 'baz';
const name = 'baz';
const query = 'true';
const instance = (new Collection(session, schema, collection)).remove(query);
const instance = collection(session, schema, name).remove(query);
expect(instance).to.be.an.instanceOf(CollectionRemove);
expect(instance._session).to.deep.equal(session);
expect(instance._schema).to.deep.equal(schema);
expect(instance._collection).to.deep.equal(collection);
expect(instance._query).to.deep.equal(query);
expect(instance.getClassName()).to.equal('CollectionRemove');
});

@@ -161,13 +159,297 @@ });

const schema = 'bar';
const collection = 'baz';
const name = 'baz';
const query = 'true';
const instance = (new Collection(session, schema, collection)).modify(query);
const instance = collection(session, schema, name).modify(query);
expect(instance).to.be.an.instanceOf(CollectionModify);
expect(instance._session).to.deep.equal(session);
expect(instance._schema).to.deep.equal(schema);
expect(instance._collection).to.deep.equal(collection);
expect(instance._query).to.deep.equal(query);
expect(instance.getClassName()).to.equal('CollectionModify');
});
});
context('replaceOne()', () => {
let crudFind, crudModify;
beforeEach('create fakes', () => {
crudFind = td.function();
crudModify = td.function();
});
it('should return the result of executing a modify operation for a given document', () => {
const collectionName = 'foo';
const documentId = 'bar';
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const state = { rows_affected: 1 };
const expected = new Result(state);
const session = { _client: { crudFind, crudModify } };
const instance = collection(session, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session), { ignoreExtraArgs: true }).thenResolve();
td.when(crudModify(schemaName, collectionName, type, criteria), { ignoreExtraArgs: true }).thenResolve(state);
return expect(instance.replaceOne(documentId, { prop: 'qux' })).to.eventually.deep.equal(expected);
});
it('should escape the id value', () => {
const collectionName = 'foo';
const documentId = 'b\\\"ar';
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "b\\\\"ar"`;
const state = { rows_affected: 1 };
const expected = new Result(state);
const session = { _client: { crudFind, crudModify } };
const instance = collection(session, { getName }, collectionName);
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session), { ignoreExtraArgs: true }).thenResolve();
td.when(crudModify(schemaName, collectionName, type, criteria), { ignoreExtraArgs: true }).thenResolve(state);
return expect(instance.replaceOne(documentId, { prop: 'qux' })).to.eventually.deep.equal(expected);
});
it('should ignore any additional `_id` property', () => {
const collectionName = 'foo';
const documentId = 'bar';
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const state = { rows_affected: 1 };
const expected = new Result(state);
const session = { _client: { crudFind, crudModify } };
const instance = collection(session, { getName }, collectionName);
const operation = {
source: {
document_path: [{
type: 1,
value: 'prop'
}]
},
operation: 3,
value: {
type: 2,
literal: {
type: 8,
v_string: {
value: 'quux'
}
}
}
};
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session), { ignoreExtraArgs: true }).thenResolve();
td.when(crudModify(schemaName, collectionName, type, criteria, [operation]), { ignoreExtraArgs: true }).thenResolve(state);
return expect(instance.replaceOne(documentId, { _id: 'qux', prop: 'quux' })).to.eventually.deep.equal(expected);
});
it('should fail if an unexpected error is thrown', () => {
const collectionName = 'foo';
const documentId = 'bar';
const schemaName = 'baz';
const error = new Error('foobar');
const session = { _client: { crudFind, crudModify } };
const instance = collection(session, { getName }, collectionName);
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session), { ignoreExtraArgs: true }).thenResolve();
td.when(crudModify(schemaName, collectionName, Client.dataModel.DOCUMENT, `$._id == "${documentId}"`), { ignoreExtraArgs: true }).thenReject(error);
return expect(instance.replaceOne(documentId, { prop: 'qux' })).to.eventually.be.rejectedWith(error);
});
});
context('addOrReplaceOne()', () => {
let crudInsert;
beforeEach('create fakes', () => {
crudInsert = td.function();
});
it('should return the result of executing a "upsert" operation for a given document', () => {
const collectionName = 'foobar';
const rows = [[JSON.stringify({ name: 'bar', _id: 'foo' })]];
const state = { doc_ids: ['foo'] };
const expected = new Result(state);
const schemaName = 'baz';
const session = { _client: { crudInsert } };
const instance = collection(session, { getName }, collectionName);
td.when(getName()).thenReturn(schemaName);
td.when(crudInsert(schemaName, collectionName, Client.dataModel.DOCUMENT, { rows }, { upsert: true })).thenResolve(state);
return expect(instance.addOrReplaceOne('foo', { name: 'bar' })).to.eventually.deep.equal(expected);
});
it('should escape the id value', () => {
const collectionName = 'foobar';
const rows = [[JSON.stringify({ name: 'bar', _id: 'fo"o' })]];
const state = { doc_ids: ['fo"o'] };
const expected = new Result(state);
const schemaName = 'baz';
const session = { _client: { crudInsert } };
const instance = collection(session, { getName }, collectionName);
td.when(getName()).thenReturn(schemaName);
td.when(crudInsert(schemaName, collectionName, Client.dataModel.DOCUMENT, { rows }, { upsert: true })).thenResolve(state);
return expect(instance.addOrReplaceOne('fo"o', { name: 'bar' })).to.eventually.deep.equal(expected);
});
it('should ignore any additional `_id` property', () => {
const collectionName = 'foobar';
const rows = [[JSON.stringify({ _id: 'foo', name: 'bar' })]];
const state = { doc_ids: ['foo'] };
const expected = new Result(state);
const schemaName = 'baz';
const session = { _client: { crudInsert } };
const instance = collection(session, { getName }, collectionName);
td.when(getName()).thenReturn(schemaName);
td.when(crudInsert(schemaName, collectionName, Client.dataModel.DOCUMENT, { rows }, { upsert: true })).thenResolve(state);
return expect(instance.addOrReplaceOne('foo', { _id: 'baz', name: 'bar' })).to.eventually.deep.equal(expected);
});
it('should fail if an unexpected error is thrown', () => {
const collectionName = 'foobar';
const rows = [[JSON.stringify({ name: 'bar', _id: 'foo' })]];
const schemaName = 'baz';
const session = { _client: { crudInsert } };
const instance = collection(session, { getName }, collectionName);
const error = new Error('bazqux');
td.when(getName()).thenReturn(schemaName);
td.when(crudInsert(schemaName, collectionName, Client.dataModel.DOCUMENT, { rows }, { upsert: true })).thenReject(error);
return expect(instance.addOrReplaceOne('foo', { name: 'bar' })).to.eventually.be.rejectedWith(error);
});
});
context('getOne()', () => {
let crudFind;
beforeEach('create fakes', () => {
crudFind = td.function();
});
it('should return the document instance if it exists', () => {
const collectionName = 'foobar';
const documentId = 'foo';
const expected = { _id: documentId, name: 'bar' };
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const session = { _client: { crudFind } };
const instance = collection(session, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session, schemaName, collectionName, type, [], criteria, any, any, any, any, td.callback([expected])), { ignoreExtraArgs: true }).thenResolve();
return expect(instance.getOne(documentId)).to.eventually.deep.equal(expected);
});
it('should escape the id value', () => {
const collectionName = 'foobar';
const documentId = 'fo\\"o';
const expected = { _id: documentId, name: 'bar' };
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "fo\\\\"o"`;
const session = { _client: { crudFind } };
const instance = collection(session, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session, schemaName, collectionName, type, [], criteria, any, any, any, any, td.callback([expected])), { ignoreExtraArgs: true }).thenResolve();
return expect(instance.getOne(documentId)).to.eventually.deep.equal(expected);
});
it('should return `null` if the document does not exist', () => {
const collectionName = 'foobar';
const documentId = 'foo';
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const session = { _client: { crudFind } };
const instance = collection(session, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudFind(session, schemaName, collectionName, type, [], criteria, any, any, any, any, td.callback([])), { ignoreExtraArgs: true }).thenResolve();
return expect(instance.getOne(documentId)).to.eventually.be.null;
});
});
context('removeOne()', () => {
let crudRemove;
beforeEach('create fakes', () => {
crudRemove = td.function();
});
it('should return the document instance if it exists', () => {
const collectionName = 'foobar';
const documentId = 'foo';
const state = { rows_affected: 1 };
const expected = new Result(state);
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const instance = collection({ _client: { crudRemove } }, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudRemove(schemaName, collectionName, type, criteria, any, any)).thenResolve(state);
return expect(instance.removeOne(documentId)).to.eventually.deep.equal(expected);
});
it('should escape the id value', () => {
const collectionName = 'foobar';
const documentId = 'fo\\"o';
const state = { rows_affected: 1 };
const expected = new Result(state);
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "fo\\\\"o"`;
const instance = collection({ _client: { crudRemove } }, { getName }, collectionName);
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudRemove(schemaName, collectionName, type, criteria, any, any)).thenResolve(state);
return expect(instance.removeOne(documentId)).to.eventually.deep.equal(expected);
});
it('should fail if an unexpected error is thrown', () => {
const collectionName = 'foobar';
const documentId = 'foo';
const schemaName = 'baz';
const type = Client.dataModel.DOCUMENT;
const criteria = `$._id == "${documentId}"`;
const instance = collection({ _client: { crudRemove } }, { getName }, collectionName);
const error = new Error('bazqux');
const any = td.matchers.anything();
td.when(getName()).thenReturn(schemaName);
td.when(crudRemove(schemaName, collectionName, type, criteria, any, any)).thenReject(error);
return expect(instance.removeOne(documentId)).to.eventually.be.rejectedWith(error);
});
});
});

@@ -5,3 +5,2 @@ 'use strict';

const CollectionAdd = require('lib/DevAPI/CollectionAdd');
const Client = require('lib/Protocol/Client');

@@ -11,2 +10,3 @@ const Result = require('lib/DevAPI/Result');

const chaiAsPromised = require('chai-as-promised');
const collectionAdd = require('lib/DevAPI/CollectionAdd');
const td = require('testdouble');

@@ -41,7 +41,13 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(collectionAdd().getClassName()).to.equal('CollectionAdd');
});
});
context('add()', () => {
it('should be fluent', () => {
const query = (new CollectionAdd()).add({ foo: 'bar' });
const query = collectionAdd().add({ foo: 'bar' });
expect(query).to.be.an.instanceOf(CollectionAdd);
expect(query.add).to.be.a('function');
});

@@ -51,5 +57,5 @@

const expected = [{ foo: 'bar' }, { foo: 'baz' }];
const query = (new CollectionAdd()).add(expected);
const query = collectionAdd().add(expected);
expect(query._document).to.deep.equal(expected);
expect(query.getDocuments()).to.deep.equal(expected);
});

@@ -59,5 +65,5 @@

const expected = [{ foo: 'bar' }, { foo: 'baz' }];
const query = (new CollectionAdd()).add(expected[0], expected[1]);
const query = collectionAdd().add(expected[0], expected[1]);
expect(query._document).to.deep.equal(expected);
expect(query.getDocuments()).to.deep.equal(expected);
});

@@ -67,5 +73,5 @@

const expected = [{ foo: 'bar' }, { foo: 'baz' }];
const query = (new CollectionAdd(fakeSession, fakeSchema, null, [{ foo: 'bar' }])).add({ foo: 'baz' });
const query = collectionAdd(fakeSession, fakeSchema, null, [{ foo: 'bar' }]).add({ foo: 'baz' });
expect(query._document).to.deep.equal(expected);
expect(query.getDocuments()).to.deep.equal(expected);
});

@@ -75,5 +81,5 @@

const expected = [{ foo: 'bar' }, { foo: 'baz' }];
const query = (new CollectionAdd()).add({ foo: 'bar' }).add({ foo: 'baz' });
const query = collectionAdd().add({ foo: 'bar' }).add({ foo: 'baz' });
expect(query._document).to.deep.equal(expected);
expect(query.getDocuments()).to.deep.equal(expected);
});

@@ -85,7 +91,8 @@ });

const documents = [{ _id: 'foo', foo: 'bar' }, { _id: 'bar', bar: 'baz' }];
const state = { ok: true };
const state = { doc_ids: ['foo', 'bar'] };
const expected = new Result(state);
const query = (new CollectionAdd(fakeSession, fakeSchema, 'collection')).add([documents[0]]).add(documents[1]);
const query = collectionAdd(fakeSession, fakeSchema, 'collection').add([documents[0]]).add(documents[1]);
const rows = [[JSON.stringify(documents[0])], [JSON.stringify(documents[1])]];
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, [[JSON.stringify(documents[0])], [JSON.stringify(documents[1])]])).thenResolve(state);
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows }, { upsert: false })).thenResolve(state);

@@ -96,8 +103,9 @@ return expect(query.execute()).to.eventually.deep.equal(expected);

it('should generate an id for documents that do not have one', () => {
const state = { ok: true };
const state = { doc_ids: ['baz'] };
const expected = new Result(state);
const query = (new CollectionAdd(fakeSession, fakeSchema, 'collection', [{ foo: 'bar' }]));
const query = collectionAdd(fakeSession, fakeSchema, 'collection', [{ foo: 'bar' }]);
const rows = [[JSON.stringify({ foo: 'bar', _id: 'baz' })]];
td.when(idGenerator()).thenReturn('baz');
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, [[JSON.stringify({ foo: 'bar', _id: 'baz' })]])).thenResolve(state);
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows }, { upsert: false })).thenResolve(state);

@@ -108,7 +116,8 @@ return expect(query.execute()).to.eventually.deep.equal(expected);

it('should append the ids of the added documents to the response state', () => {
const state = { ok: true };
const query = (new CollectionAdd(fakeSession, fakeSchema, 'collection', [{ foo: 'bar' }]));
const state = {};
const query = collectionAdd(fakeSession, fakeSchema, 'collection', [{ foo: 'bar' }]);
const rows = [[JSON.stringify({ foo: 'bar', _id: 'baz' })]];
td.when(idGenerator()).thenReturn('baz');
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, [[JSON.stringify({ foo: 'bar', _id: 'baz' })]])).thenResolve(state);
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows }, { upsert: false })).thenResolve(state);

@@ -120,3 +129,3 @@ return expect(query.execute()).to.be.fulfilled

it('should return early if no documents were provided', () => {
const query = (new CollectionAdd(fakeSession, fakeSchema, 'collection', []));
const query = collectionAdd(fakeSession, fakeSchema, 'collection', []);

@@ -135,3 +144,3 @@ td.when(idGenerator(), { ignoreExtraArgs: true }).thenReturn();

const expected = [0];
const query = (new CollectionAdd(fakeSession, fakeSchema, 'collection', [{ _id: expected[0], name: 'foo' }]));
const query = collectionAdd(fakeSession, fakeSchema, 'collection', [{ _id: expected[0], name: 'foo' }]);

@@ -148,3 +157,16 @@ td.when(idGenerator(), { ignoreExtraArgs: true }).thenReturn();

});
it('should be able to generate an "upsert" message', () => {
const doc = { _id: 'foo', name: 'bar' };
const state = { doc_ids: ['foo'] };
const expected = new Result(state);
const rows = [[JSON.stringify(doc)]];
const options = { upsert: true };
const query = collectionAdd(fakeSession, fakeSchema, 'collection', null, options).add(doc);
td.when(crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows }, options)).thenResolve(state);
return expect(query.execute()).to.eventually.deep.equal(expected);
});
});
});

@@ -6,7 +6,6 @@ 'use strict';

// npm `test` script was updated to use NODE_PATH=.
const BaseQuery = require('lib/DevAPI/BaseQuery');
const CollectionFind = require('lib/DevAPI/CollectionFind');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const collectionFind = require('lib/DevAPI/CollectionFind');
const td = require('testdouble');

@@ -18,6 +17,6 @@

describe('DevAPI Collection Find', () => {
context('constructor', () => {
it('should be an instance of BaseQuery', () => {
expect(new CollectionFind()).to.be.an.instanceof(BaseQuery);
describe('DevAPI CollectionFind', () => {
context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(collectionFind().getClassName()).to.equal('CollectionFind');
});

@@ -28,14 +27,14 @@ });

it('should be fluent', () => {
const query = (new CollectionFind()).bind('foo', 'bar');
const query = (collectionFind()).bind('foo', 'bar');
expect(query).to.be.an.instanceof(CollectionFind);
expect(query.bind).to.be.a('function');
});
it('should do nothing if no argument is provided', () => {
const query = new CollectionFind();
const bounds = Object.assign({}, query._bounds);
const query = collectionFind();
const mapping = Object.assign({}, query.getBindings());
query.bind();
expect(query._bounds).to.deep.equal(bounds);
expect(query.getBindings()).to.deep.equal(mapping);
});

@@ -45,5 +44,5 @@

const expected = { foo: 'bar' };
const query = (new CollectionFind()).bind({ foo: 'bar' });
const query = (collectionFind()).bind({ foo: 'bar' });
expect(query._bounds).to.deep.equal(expected);
expect(query.getBindings()).to.deep.equal(expected);
});

@@ -53,5 +52,5 @@

const expected = { foo: 'bar' };
const query = (new CollectionFind()).bind('foo', 'bar');
const query = (collectionFind()).bind('foo', 'bar');
expect(query._bounds).to.deep.equal(expected);
expect(query.getBindings()).to.deep.equal(expected);
});

@@ -64,7 +63,7 @@

};
const query = (new CollectionFind())
const query = (collectionFind())
.bind('foo', 'bar')
.bind('baz', 'qux');
expect(query._bounds).to.deep.equal(expected);
expect(query.getBindings()).to.deep.equal(expected);
});

@@ -74,7 +73,7 @@

const expected = { foo: 'baz' };
const query = (new CollectionFind())
const query = (collectionFind())
.bind('foo', 'bar')
.bind('foo', 'baz');
expect(query._bounds).to.deep.equal(expected);
expect(query.getBindings()).to.deep.equal(expected);
});

@@ -87,10 +86,26 @@

};
const query = (new CollectionFind())
const query = (collectionFind())
.bind('foo', 'bar')
.bind({ bar: 'baz' });
expect(query._bounds).to.deep.equal(expected);
expect(query.getBindings()).to.deep.equal(expected);
});
});
context('lockShared()', () => {
it('should set the correct locking mode', () => {
const query = (collectionFind()).lockShared();
expect(query.getLockingMode()).to.equal(1);
});
});
context('lockExclusive()', () => {
it('should set the correct locking mode', () => {
const query = (collectionFind()).lockExclusive();
expect(query.getLockingMode()).to.equal(2);
});
});
context('execute()', () => {

@@ -114,3 +129,3 @@ let fakeSession, fakeSchema;

const expected = new Result(state);
const query = (new CollectionFind(fakeSession, fakeSchema, null, '1 == 1'));
const query = collectionFind(fakeSession, fakeSchema, null, '1 == 1');
const any = td.matchers.anything();

@@ -127,3 +142,3 @@ const execute = fakeSession._client.crudFind(any, any, any, any, any, '1 == 1');

const expected = new Result(state);
const query = (new CollectionFind(fakeSession, fakeSchema)).limit(10, 0);
const query = collectionFind(fakeSession, fakeSchema).limit(10, 0);
const any = td.matchers.anything();

@@ -137,6 +152,6 @@ const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, { row_count: 10, offset: 0 });

it('should include the bounds', () => {
it('should include the value mapping', () => {
const state = { ok: true };
const expected = new Result(state);
const query = (new CollectionFind(fakeSession, fakeSchema)).bind('foo', 'bar');
const query = collectionFind(fakeSession, fakeSchema).bind('foo', 'bar');
const any = td.matchers.anything();

@@ -149,3 +164,30 @@ const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, any, any, any, { foo: 'bar' });

});
it('should set the correct default locking mode', () => {
const state = { ok: true };
const expected = new Result(state);
const query = collectionFind(fakeSession, fakeSchema);
// default locking mode
const mode = 0;
const any = td.matchers.anything();
const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, any, any, any, any, any, mode);
td.when(execute, { ignoreExtraArgs: true }).thenResolve(state);
return expect(query.execute()).eventually.deep.equal(expected);
});
it('should include the latest specified locking mode', () => {
const state = { ok: true };
const expected = new Result(state);
const query = collectionFind(fakeSession, fakeSchema).lockShared().lockExclusive();
const mode = 2;
const any = td.matchers.anything();
const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, any, any, any, any, any, mode);
td.when(execute, { ignoreExtraArgs: true }).thenResolve(state);
return expect(query.execute()).eventually.deep.equal(expected);
});
});
});

@@ -7,6 +7,6 @@ 'use strict';

const Client = require('lib/Protocol/Client');
const CollectionModify = require('lib/DevAPI/CollectionModify');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const collectionModify = require('lib/DevAPI/CollectionModify');
const td = require('testdouble');

@@ -31,5 +31,11 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(collectionModify().getClassName()).to.equal('CollectionModify');
});
});
context('execute()', () => {
it('should fail if a condition query is not provided', () => {
const operation = new CollectionModify();
const operation = collectionModify();

@@ -40,3 +46,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if a condition query is empty', () => {
const operation = new CollectionModify(null, null, null, '');
const operation = collectionModify(null, null, null, '');

@@ -47,3 +53,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if the condition is not valid', () => {
const operation = new CollectionModify(null, null, null, ' ');
const operation = collectionModify(null, null, null, ' ');

@@ -54,3 +60,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if the operation results in an error', () => {
const operation = new CollectionModify({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const operation = collectionModify({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const error = new Error('foobar');

@@ -65,3 +71,3 @@

it('should succeed if the operation succeed with the state of the operation', () => {
const operation = new CollectionModify({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const operation = collectionModify({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const state = { foo: 'bar' };

@@ -68,0 +74,0 @@ const expected = new Result(state);

@@ -7,6 +7,6 @@ 'use strict';

const Client = require('lib/Protocol/Client');
const CollectionRemove = require('lib/DevAPI/CollectionRemove');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const collectionRemove = require('lib/DevAPI/CollectionRemove');
const td = require('testdouble');

@@ -31,5 +31,11 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(collectionRemove().getClassName()).to.equal('CollectionRemove');
});
});
context('execute()', () => {
it('should fail if a condition query is not provided', () => {
const operation = new CollectionRemove();
const operation = collectionRemove();

@@ -40,3 +46,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if a condition query is empty', () => {
const operation = new CollectionRemove(null, null, null, '');
const operation = collectionRemove(null, null, null, '');

@@ -47,3 +53,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if the condition is not valid', () => {
const operation = new CollectionRemove(null, null, null, ' ');
const operation = collectionRemove(null, null, null, ' ');

@@ -54,3 +60,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('remove needs a valid condition');

it('should fail if the operation results in an error', () => {
const operation = new CollectionRemove({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const operation = collectionRemove({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const error = new Error('foobar');

@@ -65,3 +71,3 @@

it('should succeed if the operation succeed with the state of the operation', () => {
const operation = new CollectionRemove({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const operation = collectionRemove({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const state = { foo: 'bar' };

@@ -68,0 +74,0 @@ const expected = new Result(state);

@@ -100,2 +100,5 @@ 'use strict';

beforeEach('set environment variables', () => {
process.env.APPDATA = process.env.APPDATA || '%APPDATA%';
process.env.PROGRAMDATA = process.env.PROGRAMDATA || '%PROGRAMDATA%';
platform = process.platform;

@@ -108,4 +111,4 @@ Object.defineProperty(process, 'platform', { value: 'win32' });

systemConfigFile = path.resolve(os.homedir(), 'PROGRAMDATA', 'MySQL', filename);
userConfigFile = path.resolve(os.homedir(), 'APPDATA', 'MySQL', filename);
systemConfigFile = path.join(process.env.PROGRAMDATA, 'MySQL', filename);
userConfigFile = path.join(process.env.APPDATA, 'MySQL', filename);
});

@@ -279,2 +282,4 @@

beforeEach('set environment variables', () => {
process.env.APPDATA = process.env.APPDATA || '%APPDATA%';
platform = process.platform;

@@ -285,3 +290,3 @@ Object.defineProperty(process, 'platform', { value: 'win32' });

beforeEach('set scope variables', () => {
userConfigFile = path.resolve(os.homedir(), 'APPDATA', 'MySQL', 'sessions.json');
userConfigFile = path.join(process.env.APPDATA, 'MySQL', 'sessions.json');
});

@@ -377,2 +382,4 @@

beforeEach('set environment variables', () => {
process.env.APPDATA = process.env.APPDATA || '%APPDATA%';
platform = process.platform;

@@ -383,3 +390,3 @@ Object.defineProperty(process, 'platform', { value: 'win32' });

beforeEach('set scope variables', () => {
userConfigFile = path.resolve(os.homedir(), 'APPDATA', 'MySQL', 'sessions.json');
userConfigFile = path.join(process.env.APPDATA, 'MySQL', 'sessions.json');
});

@@ -386,0 +393,0 @@

@@ -6,5 +6,5 @@ 'use strict';

// npm `test` script was updated to use NODE_PATH=.
const Schema = require('lib/DevAPI/Schema');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const schema = require('lib/DevAPI/Schema');
const td = require('testdouble');

@@ -29,15 +29,15 @@

it('should return true if the schema exists in database', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
td.when(sqlStmtExecute('SHOW DATABASES LIKE ?', ['foo'], td.callback(['foo']))).thenResolve();
return expect(schema.existsInDatabase()).to.eventually.be.true;
return expect(instance.existsInDatabase()).to.eventually.be.true;
});
it('should return false if the schema does not exist in database', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
td.when(sqlStmtExecute('SHOW DATABASES LIKE ?', ['foo'], td.callback([]))).thenResolve();
return expect(schema.existsInDatabase()).to.eventually.be.false;
return expect(instance.existsInDatabase()).to.eventually.be.false;
});

@@ -48,15 +48,20 @@ });

it('should return an empty object if there are no collections', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
td.when(sqlStmtExecute('list_objects', ['foo'], td.callback([]), null, 'xplugin')).thenResolve();
return expect(schema.getCollections()).to.eventually.be.an.instanceof(Object).and.be.empty;
return expect(instance.getCollections()).to.eventually.be.an.instanceof(Object).and.be.empty;
});
it('should return an object containing the existing collections', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = instance.getCollection('foo').inspect();
td.when(sqlStmtExecute('list_objects', ['foo'], td.callback(['foo', 'COLLECTION']), null, 'xplugin')).thenResolve();
return expect(schema.getCollections()).to.eventually.deep.equal({ foo: schema.getCollection('foo') });
return expect(instance.getCollections()).to.eventually.be.fulfilled
.then(actual => {
expect(actual).to.have.all.keys(['foo']);
expect(actual.foo.inspect()).to.deep.equal(expected);
});
});

@@ -68,4 +73,4 @@ });

it('should return the existing collection if the option to re-use is enabled', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = schema.getCollection('bar');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = instance.getCollection('bar').inspect();
const error = new Error();

@@ -76,7 +81,10 @@ error.info = { code: 1050 };

return expect(schema.createCollection('bar', { ReuseExistingObject: true })).to.eventually.deep.equal(expected);
return expect(instance.createCollection('bar', { ReuseExistingObject: true })).to.eventually.be.fulfilled
.then(actual => {
expect(actual.inspect()).deep.equal(expected);
});
});
it('should fail if the option to re-use is disabled', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const error = new Error();

@@ -87,3 +95,3 @@ error.info = { code: 1050 };

return expect(schema.createCollection('bar')).to.eventually.be.rejectedWith(error);
return expect(instance.createCollection('bar')).to.eventually.be.rejectedWith(error);
});

@@ -94,12 +102,15 @@ });

it('should return a newly created collection', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = schema.getCollection('bar');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = instance.getCollection('bar').inspect();
td.when(sqlStmtExecute('create_collection', ['foo', 'bar'], null, null, 'xplugin')).thenResolve();
return expect(schema.createCollection('bar')).to.eventually.deep.equal(expected);
return expect(instance.createCollection('bar')).to.eventually.be.fulfilled
.then(actual => {
expect(actual.inspect()).to.deep.equal(expected);
});
});
it('should fail if some unexpected error is thrown', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const error = new Error();

@@ -110,7 +121,7 @@ error.info = {};

return expect(schema.createCollection('bar')).to.eventually.be.rejectedWith(error);
return expect(instance.createCollection('bar')).to.eventually.be.rejectedWith(error);
});
it('should fail if some unexpected error is thrown even if the option to re-use is enabled', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const error = new Error();

@@ -121,3 +132,3 @@ error.info = {};

return expect(schema.createCollection('bar', { ReuseExistingObject: true })).to.eventually.be.rejectedWith(error);
return expect(instance.createCollection('bar', { ReuseExistingObject: true })).to.eventually.be.rejectedWith(error);
});

@@ -130,3 +141,3 @@ });

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, name);
const instance = schema({ _client: { sqlStmtExecute } }, name);
const collection = 'bar';

@@ -136,3 +147,3 @@

return expect(schema.dropCollection(collection)).to.eventually.be.true;
return expect(instance.dropCollection(collection)).to.eventually.be.true;
});

@@ -142,3 +153,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, name);
const instance = schema({ _client: { sqlStmtExecute } }, name);
const collection = 'bar';

@@ -150,3 +161,3 @@ const error = new Error();

return expect(schema.dropCollection(collection)).to.eventually.be.true;
return expect(instance.dropCollection(collection)).to.eventually.be.true;
});

@@ -156,3 +167,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, name);
const instance = schema({ _client: { sqlStmtExecute } }, name);
const collection = 'bar';

@@ -163,3 +174,3 @@ const error = new Error('foobar');

return expect(schema.dropCollection(collection)).to.eventually.be.rejectedWith(error);
return expect(instance.dropCollection(collection)).to.eventually.be.rejectedWith(error);
});

@@ -170,25 +181,33 @@ });

it('should return an empty object if there are no tables', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
td.when(sqlStmtExecute('list_objects', ['foo'], td.callback([]), null, 'xplugin')).thenResolve();
return expect(schema.getTables()).to.eventually.be.an.instanceof(Object).and.be.empty;
return expect(instance.getTables()).to.eventually.be.an.instanceof(Object).and.be.empty;
});
it('should return an object containing the existing tables', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = schema.getTable('bar');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = instance.getTable('bar').inspect();
td.when(sqlStmtExecute('list_objects', ['foo'], td.callback(['bar', 'TABLE']), null, 'xplugin')).thenResolve();
return expect(schema.getTables()).to.eventually.deep.equal({ bar: expected });
return expect(instance.getTables()).to.eventually.be.fulfilled
.then(actual => {
expect(actual).to.have.all.keys('bar');
expect(actual.bar.inspect()).to.deep.equal(expected);
});
});
it('should return an object containing the existing views', () => {
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = schema.getTable('bar');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const expected = instance.getTable('bar').inspect();
td.when(sqlStmtExecute('list_objects', ['foo'], td.callback(['bar', 'VIEW']), null, 'xplugin')).thenResolve();
return expect(schema.getTables()).to.eventually.deep.equal({ bar: expected });
return expect(instance.getTables()).to.eventually.be.fulfilled
.then(actual => {
expect(actual).to.have.all.keys('bar');
expect(actual.bar.inspect()).to.deep.equal(expected);
});
});

@@ -200,3 +219,3 @@ });

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const table = 'bar';

@@ -206,3 +225,3 @@

return expect(schema.dropTable(table)).to.eventually.be.true;
return expect(instance.dropTable(table)).to.eventually.be.true;
});

@@ -212,3 +231,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const table = 'bar';

@@ -220,3 +239,3 @@ const error = new Error();

return expect(schema.dropTable(table)).to.eventually.be.true;
return expect(instance.dropTable(table)).to.eventually.be.true;
});

@@ -226,3 +245,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const table = 'bar';

@@ -233,3 +252,3 @@ const error = new Error('foobar');

return expect(schema.dropTable(table)).to.eventually.be.rejectedWith(error);
return expect(instance.dropTable(table)).to.eventually.be.rejectedWith(error);
});

@@ -241,3 +260,3 @@ });

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const view = 'bar';

@@ -247,3 +266,3 @@

return expect(schema.dropView(view)).to.eventually.be.true;
return expect(instance.dropView(view)).to.eventually.be.true;
});

@@ -253,3 +272,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const view = 'bar';

@@ -261,3 +280,3 @@ const error = new Error();

return expect(schema.dropView(view)).to.eventually.be.true;
return expect(instance.dropView(view)).to.eventually.be.true;
});

@@ -267,3 +286,3 @@

const name = 'foo';
const schema = new Schema({ _client: { sqlStmtExecute } }, 'foo');
const instance = schema({ _client: { sqlStmtExecute } }, 'foo');
const view = 'bar';

@@ -274,3 +293,3 @@ const error = new Error('foobar');

return expect(schema.dropView(view)).to.eventually.be.rejectedWith(error);
return expect(instance.dropView(view)).to.eventually.be.rejectedWith(error);
});

@@ -281,8 +300,8 @@ });

it('should hide internals', () => {
const schema = new Schema(null, 'foobar');
const instance = schema(null, 'foobar');
const expected = { schema: 'foobar' };
schema.inspect().should.deep.equal(expected);
instance.inspect().should.deep.equal(expected);
});
});
});

@@ -63,3 +63,3 @@ 'use strict';

expect(schema).to.be.an.instanceof(Schema);
expect(schema.getClassName()).to.equal('Schema');
});

@@ -91,7 +91,7 @@

it('should return a clean object with the session properties', () => {
const properties = { dbUser: 'foo', dbPassword: 'bar', socketFactory: { createSocket }, ssl: false };
const properties = { auth: 'PLAIN', dbUser: 'foo', dbPassword: 'bar', socketFactory: { createSocket }, ssl: false };
const session = new Session(properties);
const expected = { dbUser: 'foo' };
td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });

@@ -129,3 +129,3 @@ return expect(session.connect()).to.be.fulfilled

const session = new Session(properties);
const expected = { foo: 'bar' };
const expected = { 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] };

@@ -144,3 +144,3 @@ td.when(enableSSL({})).thenResolve();

td.when(enableSSL(), { ignoreExtraArgs: true }).thenResolve();
td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });

@@ -150,3 +150,3 @@ return expect(session.connect()).to.be.fulfilled

expect(td.explain(enableSSL).callCount).to.equal(0);
expect(session._serverCapabilities).to.be.empty;
expect(session._serverCapabilities.tls).to.be.undefined;
});

@@ -160,3 +160,3 @@ });

td.when(enableSSL({})).thenReject(new Error());
td.when(capabilitiesGet()).thenResolve({ foo: 'bar' });
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });

@@ -172,3 +172,3 @@ return expect(session.connect()).to.be.rejected

td.when(enableSSL({ foo: 'bar' })).thenResolve();
td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });

@@ -183,3 +183,3 @@ return expect(session.connect()).to.be.fulfilled;

td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ tls: true });
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'], tls: true });

@@ -197,9 +197,51 @@ return expect(session.connect()).to.be.fulfilled

td.when(enableSSL({})).thenReject(error);
td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });
return expect(session.connect()).to.be.rejected;
});
it('should select the default authentication mechanism', () => {
const properties = { dbUser: 'foo', dbPassword: 'bar', socketFactory: { createSocket } };
const session = new Session(properties);
td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'], tls: true });
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include({ auth: 'PLAIN' }));
});
it('should override the default authentication mechanism with the one provided by the user', () => {
const properties = { auth: 'MYSQL41', dbUser: 'foo', dbPassword: 'bar', socketFactory: { createSocket } };
const session = new Session(properties);
td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'], tls: true });
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include({ auth: 'MYSQL41' }));
});
});
context('insecure connections', () => {
it('should select the default authentication mechanism', () => {
const properties = { dbUser: 'foo', dbPassword: 'bar', socketFactory: { createSocket }, ssl: false };
const session = new Session(properties);
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include({ auth: 'MYSQL41' }));
});
});
context('failover', () => {
let enableSSL;
beforeEach('create fakes', () => {
enableSSL = td.function();
Client.prototype.enableSSL = enableSSL;
});
it('should failover to the next available address if the connection fails', () => {

@@ -214,3 +256,3 @@ const endpoints = [{ host: 'foo', port: 1 }, { host: 'bar', port: 2 }];

td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });
td.when(createSocket(td.matchers.contains({ host: 'foo' }))).thenReject(error);

@@ -259,3 +301,3 @@ td.when(createSocket(td.matchers.contains({ host: 'bar' }))).thenResolve(new Duplex());

td.when(capabilitiesGet()).thenResolve({});
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });
// failover restarts from the highest priority address

@@ -269,2 +311,56 @@ td.when(createSocket(), { ignoreExtraArgs: true }).thenResolve(new Duplex());

});
it('should select the default authentication mechanism for secure connections', () => {
const endpoints = [{ host: 'foo', port: 1 }, { host: 'bar', port: 2 }];
const properties = { dbUser: 'baz', dbPassword: 'qux', endpoints, socketFactory: { createSocket }, ssl: true };
const session = new Session(properties);
const expected = { auth: 'PLAIN', dbUser: 'baz', host: 'bar', port: 2, ssl: true };
const error = new Error();
error.code = 'ENOTFOUND';
td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'], tls: true });
td.when(createSocket(td.matchers.contains({ host: 'foo' }))).thenReject(error);
td.when(createSocket(td.matchers.contains({ host: 'bar' }))).thenResolve(new Duplex());
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include(expected));
});
it('should select the default authentication mechanism for insecure connections', () => {
const endpoints = [{ host: 'foo', port: 1 }, { host: 'bar', port: 2 }];
const properties = { dbUser: 'baz', dbPassword: 'qux', endpoints, socketFactory: { createSocket }, ssl: false };
const session = new Session(properties);
const expected = { auth: 'MYSQL41', dbUser: 'baz', host: 'bar', port: 2, ssl: false };
const error = new Error();
error.code = 'ENOTFOUND';
td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'] });
td.when(createSocket(td.matchers.contains({ host: 'foo' }))).thenReject(error);
td.when(createSocket(td.matchers.contains({ host: 'bar' }))).thenResolve(new Duplex());
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include(expected));
});
it('should override the default authentication mechanism with the one provided by the user', () => {
const endpoints = [{ host: 'foo', port: 1 }, { host: 'bar', port: 2 }];
const properties = { auth: 'MYSQL41', dbUser: 'baz', dbPassword: 'qux', endpoints, socketFactory: { createSocket }, ssl: true };
const session = new Session(properties);
const expected = { auth: 'MYSQL41', dbUser: 'baz', host: 'bar', port: 2, ssl: true };
const error = new Error();
error.code = 'ENOTFOUND';
td.when(enableSSL({})).thenResolve();
td.when(capabilitiesGet()).thenResolve({ 'authentication.mechanisms': ['PLAIN', 'MYSQL41'], tls: true });
td.when(createSocket(td.matchers.contains({ host: 'foo' }))).thenReject(error);
td.when(createSocket(td.matchers.contains({ host: 'bar' }))).thenResolve(new Duplex());
return expect(session.connect()).to.be.fulfilled
.then(session => expect(session.inspect()).to.deep.include(expected));
});
});

@@ -271,0 +367,0 @@ });

@@ -6,10 +6,11 @@ 'use strict';

// npm `test` script was updated to use NODE_PATH=.
const Table = require('lib/DevAPI/Table');
const TableDelete = require('lib/DevAPI/TableDelete');
const TableInsert = require('lib/DevAPI/TableInsert');
const TableSelect = require('lib/DevAPI/TableSelect');
const TableUpdate = require('lib/DevAPI/TableUpdate');
const expect = require('chai').expect;
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const table = require('lib/DevAPI/Table');
const td = require('testdouble');
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('Table', () => {

@@ -29,5 +30,5 @@ let sqlStmtExecute, getName;

it('should return the table name', () => {
const table = new Table(null, null, 'foobar');
const instance = table(null, null, 'foobar');
expect(table.getName()).to.equal('foobar');
expect(instance.getName()).to.equal('foobar');
});

@@ -38,3 +39,3 @@ });

it('should return true if the table exists in database', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const query = 'SELECT COUNT(*) cnt FROM information_schema.TABLES WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';

@@ -45,7 +46,7 @@

return expect(table.existsInDatabase()).to.eventually.be.true;
return expect(instance.existsInDatabase()).to.eventually.be.true;
});
it('should return false if the table does not exist in database', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const query = 'SELECT COUNT(*) cnt FROM information_schema.TABLES WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';

@@ -56,3 +57,3 @@

return expect(table.existsInDatabase()).to.eventually.be.false;
return expect(instance.existsInDatabase()).to.eventually.be.false;
});

@@ -63,3 +64,3 @@ });

it('should return true if the table exists in database', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const query = 'SELECT COUNT(*) cnt FROM information_schema.VIEWS WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';

@@ -70,7 +71,7 @@

return expect(table.isView()).to.eventually.be.true;
return expect(instance.isView()).to.eventually.be.true;
});
it('should return false if the table does not exist in database', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const query = 'SELECT COUNT(*) cnt FROM information_schema.VIEWS WHERE TABLE_CATALOG = ? AND TABLE_SCHEMA = ? AND TABLE_NAME = ? HAVING COUNT(*) = 1';

@@ -81,3 +82,3 @@

return expect(table.isView()).to.eventually.be.false;
return expect(instance.isView()).to.eventually.be.false;
});

@@ -88,5 +89,5 @@ });

it('should return an instance of the proper class', () => {
const instance = (new Table()).select();
const instance = table().select();
expect(instance).to.be.an.instanceof(TableSelect);
expect(instance.getClassName()).to.equal('TableSelect');
});

@@ -96,5 +97,5 @@

const expressions = ['foo', 'bar'];
const instance = (new Table()).select(expressions);
const instance = table().select(expressions);
expect(instance._projection).to.deep.equal(expressions);
expect(instance.getProjection()).to.deep.equal(expressions);
});

@@ -104,5 +105,5 @@

const expressions = ['foo', 'bar'];
const instance = (new Table()).select(expressions[0], expressions[1]);
const instance = table().select(expressions[0], expressions[1]);
expect(instance._projection).to.deep.equal(expressions);
expect(instance.getProjection()).to.deep.equal(expressions);
});

@@ -113,5 +114,5 @@ });

it('should return an instance of the proper class', () => {
const instance = (new Table()).insert([]);
const instance = table().insert([]);
expect(instance).to.be.an.instanceof(TableInsert);
expect(instance.getClassName()).to.equal('TableInsert');
});

@@ -121,5 +122,5 @@

const expressions = ['foo', 'bar'];
const instance = (new Table()).insert(expressions);
const instance = table().insert(expressions);
expect(instance._fields).to.deep.equal(expressions);
expect(instance.getFields()).to.deep.equal(expressions);
});

@@ -129,5 +130,5 @@

const expressions = ['foo', 'bar'];
const instance = (new Table()).insert(expressions[0], expressions[1]);
const instance = table().insert(expressions[0], expressions[1]);
expect(instance._fields).to.deep.equal(expressions);
expect(instance.getFields()).to.deep.equal(expressions);
});

@@ -137,11 +138,11 @@

const expressions = ['foo', 'bar'];
const instance = (new Table()).insert({ foo: 'baz', bar: 'qux' });
const instance = table().insert({ foo: 'baz', bar: 'qux' });
expect(instance._fields).to.deep.equal(expressions);
expect(instance.getFields()).to.deep.equal(expressions);
});
it('should throw an error if the fields are invalid', () => {
const table = new Table();
const instance = table();
expect(() => table.insert()).to.throw(Error);
expect(() => instance.insert()).to.throw(Error);
});

@@ -152,3 +153,3 @@ });

it('should return the number of records found', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const count = 3;

@@ -159,7 +160,7 @@

return expect(table.count()).to.eventually.equal(count);
return expect(instance.count()).to.eventually.equal(count);
});
it('should fail if an expected error is thrown', () => {
const table = new Table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const instance = table({ _client: { sqlStmtExecute } }, { getName }, 'foo');
const error = new Error('foobar');

@@ -170,3 +171,3 @@

return expect(table.count()).to.eventually.be.rejectedWith(error);
return expect(instance.count()).to.eventually.be.rejectedWith(error);
});

@@ -177,3 +178,3 @@ });

it('should hide internals', () => {
const table = new Table(null, { getName }, 'foo');
const instance = table(null, { getName }, 'foo');
const expected = { schema: 'bar', table: 'foo' };

@@ -183,3 +184,3 @@

expect(table.inspect()).to.deep.equal(expected);
expect(instance.inspect()).to.deep.equal(expected);
});

@@ -192,12 +193,7 @@ });

const schema = 'bar';
const table = 'baz';
const name = 'baz';
const query = 'true';
const instance = (new Table(session, schema, table)).delete(query);
const instance = (table(session, schema, name)).delete(query);
expect(instance).to.be.an.instanceOf(TableDelete);
expect(instance._session).to.deep.equal(session);
expect(instance._schema).to.deep.equal(schema);
expect(instance._table).to.deep.equal(table);
expect(instance._query).to.deep.equal(query);
expect(instance.where).to.be.a('function');
expect(instance.getClassName()).to.equal('TableDelete');
});

@@ -210,14 +206,9 @@ });

const schema = 'bar';
const table = 'baz';
const name = 'baz';
const query = 'true';
const instance = (new Table(session, schema, table)).update(query);
const instance = (table(session, schema, name)).update(query);
expect(instance).to.be.an.instanceOf(TableUpdate);
expect(instance._session).to.deep.equal(session);
expect(instance._schema).to.deep.equal(schema);
expect(instance._table).to.deep.equal(table);
expect(instance._query).to.deep.equal(query);
expect(instance.where).to.be.a('function');
expect(instance.getClassName()).to.equal('TableUpdate');
});
});
});

@@ -7,6 +7,6 @@ 'use strict';

const Client = require('lib/Protocol/Client');
const TableDelete = require('lib/DevAPI/TableDelete');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const tableDelete = require('lib/DevAPI/TableDelete');
const td = require('testdouble');

@@ -31,8 +31,14 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(tableDelete().getClassName()).to.equal('TableDelete');
});
});
context('where()', () => {
it('should set the operation condition', () => {
const query = 'foo';
const operation = (new TableDelete()).where(query);
const operation = tableDelete().where(query);
expect(operation._query).to.equal(query);
expect(operation.getCriteria()).to.equal(query);
});

@@ -43,9 +49,7 @@ });

it('should fail if a condition query is not provided', () => {
const operation = new TableDelete();
return expect(operation.execute()).to.eventually.be.rejectedWith('delete needs a valid condition');
return expect(tableDelete().execute()).to.eventually.be.rejectedWith('delete needs a valid condition');
});
it('should fail if a condition query is empty', () => {
const operation = new TableDelete(null, null, null, '');
const operation = tableDelete(null, null, null, '');

@@ -56,3 +60,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('delete needs a valid condition');

it('should fail if a condition query is not valid', () => {
const operation = new TableDelete(null, null, null, ' ');
const operation = tableDelete(null, null, null, ' ');

@@ -63,3 +67,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('delete needs a valid condition');

it('should fail if the operation results in an error', () => {
const operation = new TableDelete({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const operation = tableDelete({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const error = new Error('foobar');

@@ -74,3 +78,3 @@

it('should succeed if the operation succeed with the state of the operation', () => {
const operation = new TableDelete({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const operation = tableDelete({ _client: { crudRemove } }, { getName }, 'foo', 'bar');
const state = { foo: 'bar' };

@@ -77,0 +81,0 @@ const expected = new Result(state);

@@ -8,4 +8,4 @@ 'use strict';

const Result = require('lib/DevAPI/Result');
const TableInsert = require('lib/DevAPI/TableInsert');
const expect = require('chai').expect;
const tableInsert = require('lib/DevAPI/TableInsert');
const td = require('testdouble');

@@ -32,2 +32,8 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(tableInsert().getClassName()).to.equal('TableInsert');
});
});
context('execute()', () => {

@@ -37,6 +43,7 @@ it('should include the fields and projection values', () => {

const expected = new Result(state);
const query = new TableInsert(fakeSession, fakeSchema, 'table', ['foo', 'bar']).values(['baz', 'qux']);
const projection = [{ name: 'foo' }, { name: 'bar' }];
const query = tableInsert(fakeSession, fakeSchema, 'table', ['foo', 'bar']).values(['baz', 'qux']);
const columns = [{ name: 'foo' }, { name: 'bar' }];
const rows = [['baz', 'qux']];
td.when(crudInsert('schema', 'table', Client.dataModel.TABLE, [['baz', 'qux']], projection)).thenResolve(state);
td.when(crudInsert('schema', 'table', Client.dataModel.TABLE, { columns, rows })).thenResolve(state);

@@ -49,5 +56,5 @@ return query.execute().should.eventually.deep.equal(expected);

it('should be fluent', () => {
const query = (new TableInsert(null, null, null, ['foo'])).values('bar');
const query = tableInsert(null, null, null, ['foo']).values('bar');
expect(query).to.be.an.instanceof(TableInsert);
expect(query.values).to.be.a('function');
});

@@ -57,5 +64,5 @@

const values = ['baz', 'qux'];
const query = (new TableInsert(null, null, null, ['foo', 'bar'])).values(values);
const query = tableInsert(null, null, null, ['foo', 'bar']).values(values);
expect(query._rows).to.deep.equal([values]);
expect(query.getRows()).to.deep.equal([values]);
});

@@ -65,5 +72,5 @@

const values = ['baz', 'qux'];
const query = (new TableInsert(null, null, null, ['foo', 'bar'])).values(values[0], values[1]);
const query = tableInsert(null, null, null, ['foo', 'bar']).values(values[0], values[1]);
expect(query._rows).to.deep.equal([values]);
expect(query.getRows()).to.deep.equal([values]);
});

@@ -73,3 +80,3 @@

const values = ['baz', 'qux'];
const query = (new TableInsert(null, null, null, ['foo']));
const query = tableInsert(null, null, null, ['foo']);

@@ -76,0 +83,0 @@ expect(() => query.values(values[0], values[1])).to.throw(Error);

@@ -7,7 +7,6 @@ 'use strict';

// npm `test` script was updated to use NODE_PATH=.
const BaseQuery = require('lib/DevAPI/BaseQuery');
const Result = require('lib/DevAPI/Result');
const TableSelect = require('lib/DevAPI/TableSelect');
const expect = require('chai').expect;
const parseExpressionInputs = require('lib/DevAPI/Util/parseExpressionInputs');
const tableSelect = require('lib/DevAPI/TableSelect');
const td = require('testdouble');

@@ -34,8 +33,24 @@

context('constructor', () => {
it('should be an instance of BaseQuery', () => {
expect(new TableSelect()).to.be.an.instanceof(BaseQuery);
context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(tableSelect().getClassName()).to.equal('TableSelect');
});
});
context('lockShared()', () => {
it('should set the correct locking mode', () => {
const query = tableSelect().lockShared();
expect(query.getLockingMode()).to.equal(1);
});
});
context('lockExclusive()', () => {
it('should set the correct locking mode', () => {
const query = tableSelect().lockExclusive();
expect(query.getLockingMode()).to.equal(2);
});
});
context('execute()', () => {

@@ -45,3 +60,3 @@ it('should acknowledge the projection values', () => {

const expected = new Result(state);
const query = new TableSelect(fakeSession, fakeSchema, 'table', ['foo', 'bar']);
const query = tableSelect(fakeSession, fakeSchema, 'table', ['foo', 'bar']);

@@ -53,2 +68,29 @@ const call = crudFind(fakeSession, 'schema', 'table', Client.dataModel.TABLE, parseExpressionInputs(['foo', 'bar']));

});
it('should set the correct default locking mode', () => {
const state = { ok: true };
const expected = new Result(state);
const query = tableSelect(fakeSession, fakeSchema);
// default locking mode
const mode = 0;
const any = td.matchers.anything();
const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, any, any, any, any, any, mode);
td.when(execute, { ignoreExtraArgs: true }).thenResolve(state);
return expect(query.execute()).eventually.deep.equal(expected);
});
it('should include the latest specified locking mode', () => {
const state = { ok: true };
const expected = new Result(state);
const query = tableSelect(fakeSession, fakeSchema).lockShared().lockExclusive();
const mode = 2;
const any = td.matchers.anything();
const execute = fakeSession._client.crudFind(any, any, any, any, any, any, any, any, any, any, any, any, any, any, mode);
td.when(execute, { ignoreExtraArgs: true }).thenResolve(state);
return expect(query.execute()).eventually.deep.equal(expected);
});
});

@@ -59,3 +101,3 @@

const expected = 'SELECT foo, bar FROM schema.table';
const query = new TableSelect(fakeSession, fakeSchema, 'table', ['foo', 'bar']);
const query = tableSelect(fakeSession, fakeSchema, 'table', ['foo', 'bar']);

@@ -67,3 +109,3 @@ expect(query.getViewDefinition()).to.equal(expected);

const expected = 'SELECT * FROM schema.table WHERE foo = "baz"';
const query = (new TableSelect(fakeSession, fakeSchema, 'table', ['*'])).where('foo = "baz"');
const query = tableSelect(fakeSession, fakeSchema, 'table', ['*']).where('foo = "baz"');

@@ -75,3 +117,3 @@ expect(query.getViewDefinition()).to.equal(expected);

const expected = 'SELECT foo FROM schema.table ORDER BY bar';
const query = (new TableSelect(fakeSession, fakeSchema, 'table', ['foo'])).orderBy(['bar']);
const query = tableSelect(fakeSession, fakeSchema, 'table', ['foo']).orderBy(['bar']);

@@ -84,5 +126,5 @@ expect(query.getViewDefinition()).to.equal(expected);

it('should be fluent', () => {
const query = (new TableSelect()).orderBy();
const query = tableSelect().orderBy();
expect(query).to.be.an.instanceof(TableSelect);
expect(query.orderBy).to.be.a('function');
});

@@ -92,5 +134,5 @@

const parameters = ['foo desc', 'bar desc'];
const query = (new TableSelect()).orderBy(parameters);
const query = tableSelect().orderBy(parameters);
expect(query._orderby).to.deep.equal(parameters);
expect(query.getOrderBy()).to.deep.equal(parameters);
});

@@ -100,5 +142,5 @@

const parameters = ['foo desc', 'bar desc'];
const query = (new TableSelect()).orderBy(parameters[0], parameters[1]);
const query = tableSelect().orderBy(parameters[0], parameters[1]);
expect(query._orderby).to.deep.equal(parameters);
expect(query.getOrderBy()).to.deep.equal(parameters);
});

@@ -109,5 +151,5 @@ });

it('should be fluent', () => {
const query = (new TableSelect()).groupBy();
const query = tableSelect().groupBy();
expect(query).to.be.an.instanceof(TableSelect);
expect(query.groupBy).to.be.a('function');
});

@@ -117,5 +159,5 @@

const grouping = ['foo', 'bar'];
const query = (new TableSelect()).groupBy(grouping);
const query = tableSelect().groupBy(grouping);
expect(query._groupby).to.deep.equal(grouping);
expect(query.getGroupBy()).to.deep.equal(grouping);
});

@@ -125,7 +167,7 @@

const grouping = ['foo', 'bar'];
const query = (new TableSelect()).groupBy(grouping[0], grouping[1]);
const query = tableSelect().groupBy(grouping[0], grouping[1]);
expect(query._groupby).to.deep.equal(grouping);
expect(query.getGroupBy()).to.deep.equal(grouping);
});
});
});

@@ -7,6 +7,6 @@ 'use strict';

const Client = require('lib/Protocol/Client');
const TableUpdate = require('lib/DevAPI/TableUpdate');
const Result = require('lib/DevAPI/Result');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const tableUpdate = require('lib/DevAPI/TableUpdate');
const td = require('testdouble');

@@ -31,8 +31,14 @@

context('getClassName()', () => {
it('should return the correct class name (to avoid duck typing)', () => {
expect(tableUpdate().getClassName()).to.equal('TableUpdate');
});
});
context('where()', () => {
it('should set the operation condition', () => {
const query = 'foo';
const operation = (new TableUpdate()).where(query);
const operation = tableUpdate().where(query);
expect(operation._query).to.equal(query);
expect(operation.getCriteria()).to.equal(query);
});

@@ -43,3 +49,3 @@ });

it('should fail if a condition query is not provided', () => {
const operation = new TableUpdate();
const operation = tableUpdate();

@@ -50,3 +56,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('update needs a valid condition');

it('should fail if a condition query is empty', () => {
const operation = new TableUpdate(null, null, null, '');
const operation = tableUpdate(null, null, null, '');

@@ -57,3 +63,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('update needs a valid condition');

it('should fail if a condition query is not valid', () => {
const operation = new TableUpdate(null, null, null, ' ');
const operation = tableUpdate(null, null, null, ' ');

@@ -64,3 +70,3 @@ return expect(operation.execute()).to.eventually.be.rejectedWith('update needs a valid condition');

it('should fail if the operation results in an error', () => {
const operation = new TableUpdate({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const operation = tableUpdate({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const error = new Error('foobar');

@@ -75,3 +81,3 @@

it('should succeed if the operation succeed with the state of the operation', () => {
const operation = new TableUpdate({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const operation = tableUpdate({ _client: { crudModify } }, { getName }, 'foo', 'bar');
const state = { foo: 'bar' };

@@ -78,0 +84,0 @@ const expected = new Result(state);

@@ -32,3 +32,9 @@ 'use strict';

});
it('should allow empty or undefined inputs', () => {
['', undefined, null].forEach(input => {
return expect(parseExpressionInputs(input)).to.be.empty;
});
});
});
});

@@ -12,2 +12,3 @@ 'use strict';

const expected = {
auth: '',
dbUser: 'user',

@@ -33,2 +34,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -54,2 +56,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -75,2 +78,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -96,2 +100,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: undefined,

@@ -117,2 +122,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -138,2 +144,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -159,2 +166,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: 'user',

@@ -180,2 +188,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -201,2 +210,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: undefined,

@@ -223,2 +233,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: 'user',

@@ -248,2 +259,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -298,2 +310,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -319,2 +332,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -340,2 +354,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -361,2 +376,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -382,2 +398,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -403,2 +420,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -426,2 +444,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -447,2 +466,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -467,2 +487,25 @@ dbPassword: 'password',

context('authentication method', () => {
it('should parse an URI with a specific authentication method', () => {
const expected = {
auth: 'AUTHMETHOD',
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: 'localhost',
port: 33060,
socket: undefined
}],
schema: undefined,
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
expect(parseUri('mysqlx://user:password@localhost:33060?auth=authMethod')).to.deep.equal(expected);
});
});
it('should throw an error if the host is not valid', () => {

@@ -476,2 +519,3 @@ expect(() => parseUri('mysql#x')).to.throw(Error, 'Invalid URI');

const expected = {
auth: '',
dbUser: 'user',

@@ -497,2 +541,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: 'user',

@@ -518,2 +563,3 @@ dbPassword: '',

const expected = {
auth: '',
dbUser: 'user',

@@ -539,2 +585,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: 'user',

@@ -560,2 +607,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -581,2 +629,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -602,2 +651,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -623,2 +673,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: undefined,

@@ -645,2 +696,3 @@ dbPassword: undefined,

const expected = {
auth: '',
dbUser: 'user',

@@ -674,2 +726,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -725,2 +778,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -746,2 +800,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -767,2 +822,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -788,2 +844,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -809,2 +866,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -830,2 +888,3 @@ dbPassword: 'password',

const expected = {
auth: '',
dbUser: 'user',

@@ -850,40 +909,67 @@ dbPassword: 'password',

it('should parse a connection string with a pct-encoded UNIX socket', () => {
const expected = {
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: undefined,
port: undefined,
socket: './path/to/socket'
}],
schema: undefined,
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
context('local sockets', () => {
it('should parse a connection string with a pct-encoded UNIX socket', () => {
const expected = {
auth: '',
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: undefined,
port: undefined,
socket: './path/to/socket'
}],
schema: undefined,
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
expect(parseUri('user:password@.%2Fpath%2Fto%2Fsocket')).to.deep.equal(expected);
expect(parseUri('user:password@.%2Fpath%2Fto%2Fsocket')).to.deep.equal(expected);
});
it('should parse an URI with a custom-encoded UNIX socket', () => {
const expected = {
auth: '',
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: undefined,
port: undefined,
socket: './path/to/socket'
}],
schema: 'schema',
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
expect(parseUri('user:password@(./path/to/socket)/schema')).to.deep.equal(expected);
});
});
it('should parse an URI with a custom-encoded UNIX socket', () => {
const expected = {
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: undefined,
port: undefined,
socket: './path/to/socket'
}],
schema: 'schema',
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
context('authentication method', () => {
it('should parse an URI with a specific authentication method', () => {
const expected = {
auth: 'AUTHMETHOD',
dbUser: 'user',
dbPassword: 'password',
endpoints: [{
host: 'localhost',
port: 33060,
socket: undefined
}],
schema: undefined,
ssl: true,
sslOptions: {
ca: undefined,
crl: undefined
}
};
expect(parseUri('user:password@(./path/to/socket)/schema')).to.deep.equal(expected);
expect(parseUri('user:password@localhost:33060?auth=authMethod')).to.deep.equal(expected);
});
});

@@ -890,0 +976,0 @@

@@ -8,3 +8,3 @@ 'use strict';

describe('@connections-secure-by-default parseSecurityOptions', () => {
describe('parseSecurityOptions', () => {
it('should enable ssl if any of the security related properties are provided', () => {

@@ -11,0 +11,0 @@ // TODO(Rui): add `ssl-mode=VERIFY_CA`, `ssl-mode=VERIFY_IDENTITY` and/or `ssl-mode=VERIFY_CRL`?

@@ -34,2 +34,26 @@ "use strict";

{
should: 'allow lowercase null',
in: 'null',
exp: {
type: 2,
literal: Datatype.encodeScalar(null)
}
},
{
should: 'allow uppercase null',
in: 'NULL',
exp: {
type: 2,
literal: Datatype.encodeScalar(null)
}
},
{
should: 'allow zero',
in: '0',
exp: {
type: 2,
literal: Datatype.encodeScalar(0)
}
},
{
should: 'allow whitespaces within strings',

@@ -62,2 +86,20 @@ in: '"foo bar"',

{
should: 'allow empty quoted strings',
in: "''",
exp: {
type: 2,
literal: Datatype.encodeScalar('')
}
},
{
should: 'allow empty double quoted strings',
in: '""',
exp: {
type: 2,
literal: Datatype.encodeScalar('')
}
},
{
should: 'allow escaped quotes within strings',

@@ -170,2 +212,19 @@ in: '"foo\\\"bar"',

{
should: 'allow negation',
in: '!false',
exp: {
type: 5,
operator: {
name: '!',
param: [
{
type: 2,
literal: Datatype.encodeScalar(false)
}
]
}
}
},
{
should: 'allow functions without arguments',

@@ -342,2 +401,2 @@ in: 'concat()',

});
});
});

@@ -9,3 +9,4 @@ 'use strict';

const SqlResultHandler = require('lib/Protocol/ResponseHandler').SqlResultHandler;
const expect = require('chai').expect;
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const isEqual = require('lodash.isequal');

@@ -15,10 +16,15 @@ const proxyquire = require('proxyquire');

chai.use(chaiAsPromised);
const expect = chai.expect;
describe('Client', () => {
let on, sendMessage;
let on, sendMessage, fakeSendMessage;
beforeEach('create fakes', () => {
on = td.function();
fakeSendMessage = td.function();
sendMessage = SqlResultHandler.prototype.sendMessage;
SqlResultHandler.prototype.sendMessage = td.function();
SqlResultHandler.prototype.sendMessage = fakeSendMessage;
});

@@ -36,3 +42,3 @@

expect(client._stream).to.equal(stream);
return expect(client._stream).to.equal(stream);
});

@@ -43,3 +49,3 @@

expect(client._workQueue).to.be.an.instanceof(WorkQueue);
return expect(client._workQueue).to.be.an.instanceof(WorkQueue);
});

@@ -50,3 +56,3 @@

expect(client._danglingFragment).to.be.false;
return expect(client._danglingFragment).to.be.false;
});

@@ -59,6 +65,7 @@

td.when(on('data')).thenCallback('foo');
td.when(handleNetworkFragment('foo')).thenReturn();
Client.call({ handleNetworkFragment }, stream);
td.verify(handleNetworkFragment('foo'), { times: 1 });
return expect(td.explain(handleNetworkFragment).callCount).to.equal(1);
});

@@ -71,6 +78,7 @@

td.when(on('close')).thenCallback();
td.when(handleServerClose('foo')).thenReturn();
Client.call({ handleServerClose }, stream);
td.verify(handleServerClose(), { times: 1 });
return expect(td.explain(handleServerClose).callCount).to.equal(1);
});

@@ -225,7 +233,7 @@ });

// FIXME(ruiquelhas): test the promise interface.
context('crudFind()', () => {
it('should encode grouping expressions correctly', () => {
const client = new Client({ on });
const expected = [{
const expected = { ok: true };
const grouping = [{
type: 1,

@@ -241,22 +249,59 @@ identifier: {

}];
const match = td.matchers.argThat(data => isEqual(data.grouping, expected));
const message = new Buffer('foobar');
const encodeMessage = td.function();
const matcher = td.matchers.argThat(data => isEqual(data.grouping, grouping));
client.encodeMessage = td.function();
client.crudFind(null, null, null, null, null, null, ['foo', 'bar']);
client.encodeMessage = encodeMessage;
td.verify(client.encodeMessage(Messages.ClientMessages.CRUD_FIND, match), { times: 1 });
td.when(encodeMessage(Messages.ClientMessages.CRUD_FIND, matcher)).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenResolve(expected);
return expect(client.crudFind(null, null, null, null, null, null, ['foo', 'bar'])).to.eventually.deep.equal(expected);
});
it('should send encoded message to the server', () => {
const handler = new SqlResultHandler();
it('shoud encode a specific row locking mode', () => {
const expected = { ok: true };
const client = new Client({ on });
const encodeMessage = td.function();
const mode = 1;
const message = new Buffer('foobar');
client.encodeMessage = td.function();
client.encodeMessage = encodeMessage;
td.when(client.encodeMessage(td.matchers.anything(), td.matchers.anything())).thenReturn('foobar');
td.when(encodeMessage(Messages.ClientMessages.CRUD_FIND, td.matchers.contains({ locking: mode }))).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenResolve(expected);
client.crudFind();
return expect(client.crudFind(null, null, null, null, null, null, null, null, null, null, null, null, null, null, mode)).to.eventually.deep.equal(expected);
});
td.verify(handler.sendMessage(client._workQueue, client._stream, 'foobar'), { times: 1 });
it('should not encode the default row locking mode', () => {
const expected = { ok: true };
const client = new Client({ on });
const encodeMessage = td.function();
const mode = 0;
const message = new Buffer('foobar');
client.encodeMessage = encodeMessage;
const matcher = td.matchers.argThat(data => !data.locking);
td.when(encodeMessage(Messages.ClientMessages.CRUD_FIND, matcher)).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenResolve(expected);
return expect(client.crudFind(null, null, null, null, null, null, null, null, null, null, null, null, null, null, mode)).to.eventually.deep.equal(expected);
});
it('should fail if the message cannot be sent', () => {
const error = new Error('foobar');
const client = new Client({ on });
const encodeMessage = td.function();
const message = new Buffer('foobar');
client.encodeMessage = encodeMessage;
td.when(encodeMessage(Messages.ClientMessages.CRUD_FIND, td.matchers.anything())).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenReject(error);
return expect(client.crudFind()).to.eventually.be.rejectedWith(error);
});
});

@@ -266,18 +311,23 @@

it('should encode field names correctly', () => {
const expected = { ok: true };
const client = new Client({ on });
const expected = ['foo', 'bar'];
const handler = new SqlResultHandler();
const match = td.matchers.argThat(data => isEqual(data.projection, expected));
const columns = ['foo', 'bar'];
const message = new Buffer('foobar');
const encodeMessage = td.function();
const match = td.matchers.argThat(data => isEqual(data.projection, columns));
client.encodeMessage = td.function();
client.encodeMessage = encodeMessage;
td.when(handler.sendMessage(), { ignoreExtraArgs: true }).thenResolve();
td.when(encodeMessage(Messages.ClientMessages.CRUD_INSERT, match)).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenResolve(expected);
return client.crudInsert(null, null, null, [['baz', 'qux']], expected)
.then(() => td.verify(client.encodeMessage(Messages.ClientMessages.CRUD_INSERT, match), { times: 1 }));
return expect(client.crudInsert(null, null, null, { columns, rows: [['baz', 'qux']] })).to.eventually.deep.equal(expected);
});
it('should encode row values correctly', () => {
const expected = { ok: true };
const client = new Client({ on });
const expected = [{
const message = new Buffer('foobar');
const encodeMessage = td.function();
const rows = [{
field: [{

@@ -301,56 +351,25 @@ type: 2,

}];
const handler = new SqlResultHandler();
const match = td.matchers.argThat(data => isEqual(data.row, expected));
const matcher = td.matchers.argThat(data => isEqual(data.row, rows));
td.when(handler.sendMessage(), { ignoreExtraArgs: true }).thenResolve();
client.encodeMessage = encodeMessage;
client.encodeMessage = td.function();
td.when(encodeMessage(Messages.ClientMessages.CRUD_INSERT, matcher)).thenReturn(message);
td.when(fakeSendMessage(client._workQueue, client._stream, message)).thenResolve(expected);
return client.crudInsert(null, null, null, [['foo', 'bar']])
.then(() => td.verify(client.encodeMessage(Messages.ClientMessages.CRUD_INSERT, match), { times: 1 }));
return expect(client.crudInsert(null, null, null, { rows: [['foo', 'bar']] })).to.eventually.deep.equal(expected);
});
it('should send encoded message to the server', () => {
it('should fail if the message cannot be sent', () => {
const client = new Client({ on });
const expected = { foo: 'bar' };
const handler = new SqlResultHandler();
client.encodeMessage = td.function();
td.when(client.encodeMessage(), { ignoreExtraArgs: true }).thenReturn('foobar');
td.when(handler.sendMessage(client._workQueue, client._stream, 'foobar')).thenResolve(expected);
return client.crudInsert(null, null, null, [[]]).should.become(expected);
});
it('should throw error if no documents are provided', () => {
const client = new Client({ on });
const handler = new SqlResultHandler();
client.encodeMessage = td.function();
return client.crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, {})
.catch(err => {
expect(err).to.be.an.instanceof(Error);
expect(err.message).to.equal('No document provided for Crud::Insert');
td.verify(client.encodeMessage(), { ignoreExtraArgs: true, times: 0 });
td.verify(handler.sendMessage(), { ignoreExtraArgs: true, times: 0 });
});
});
// TODO(rui.quelhas): add the same test for `crudFind`
it('should throw error if the message cannot be sent', () => {
const client = new Client({ on });
const error = new Error('foo');
const handler = new SqlResultHandler();
const encodeMessage = td.function();
client.encodeMessage = td.function();
client.encodeMessage = encodeMessage;
td.when(client.encodeMessage(), { ignoreExtraArgs: true }).thenReturn();
td.when(handler.sendMessage(), { ignoreExtraArgs: true }).thenReject(error);
td.when(encodeMessage(), { ignoreExtraArgs: true }).thenReturn();
td.when(fakeSendMessage(), { ignoreExtraArgs: true }).thenReject(error);
return client.crudInsert(null, null, null, [['foo', 'bar']]).should.be.rejectedWith(error);
return expect(client.crudInsert(null, null, null, { rows: [['foo', 'bar']] })).to.eventually.be.rejectedWith(error);
});
});
});

@@ -51,4 +51,4 @@ 'use strict';

const promise = Promise.all([
protocol.crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, [[{ _id: 123 }]]),
protocol.crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, [[{ _id: 456 }]])
protocol.crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows: [[{ _id: 123 }]] }),
protocol.crudInsert('schema', 'collection', Client.dataModel.DOCUMENT, { rows: [[{ _id: 456 }]] })
]);

@@ -55,0 +55,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc