sails-hook-orm
Version:
The ORM hook from Sails core.
168 lines (154 loc) • 7.65 kB
JavaScript
/**
* Module dependencies
*/
var assert = require('assert');
var util = require('util');
var _ = require('@sailshq/lodash');
var helpSendNativeQuery = require('./help-send-native-query');
/**
* helpSendStatement()
*
* Send a WLQL "statement" (stage 4 query) to the database and return the results.
*
* > This utility is for a datastore (RDI) method. Before attempting to use this,
* > the datastore method should guarantee that the adapter (via its driver) actually
* > supports all the necessary pieces.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* @param {Dictionary} options
* @required {Dictionary} statement
* @required {Ref} driver
* @either
* @or {Ref} manager
* @or {Ref} connection
* @optional {Dictionary} meta
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* @param {Function} done
* @param {Error?} err
* @param {JSON} parsedResult
*/
module.exports = function helpSendStatement(options, done){
assert(_.isObject(options.statement) && !_.isArray(options.statement) && !_.isFunction(options.statement));
assert(options.driver);
assert(options.manager || options.connection);
assert(!options.meta || options.meta && _.isObject(options.meta));
// ╔═╗╦ ╔═╗╔═╗╔═╗╦╔═╗╦ ╦ ┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┐┌┌┬┐
// ║ ║ ╠═╣╚═╗╚═╗║╠╣ ╚╦╝ └─┐ │ ├─┤ │ ├┤ │││├┤ │││ │
// ╚═╝╩═╝╩ ╩╚═╝╚═╝╩╚ ╩ └─┘ ┴ ┴ ┴ ┴ └─┘┴ ┴└─┘┘└┘ ┴
// Examine the statement to figure out its "query type".
// (This is used below when we parse the raw result returned from the compiled native query.)
//
// > If the statement cannot be identified as any recognized "query type", then
// > we'll bail with an error. (Besides providing important information we need below,
// > this step is also a sanity check that ensures the baseline sanity of the provided
// > statement before attempting to pass it in to the driver's `.compileStatement()`
// > method.)
// >
// > For full list of query types, see:
// > • https://github.com/treelinehq/waterline-query-docs/blob/8f0228cbb05fca72693cc2cb3747e05593b8063c/docs/results.md
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FUTURE: Since it's generic and would be helpful to use from adapters as well,
// extrapolate this logic into wl-utils.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var queryType;
if (_.has(options.statement, 'select')) {
queryType = 'select';
}
else if (_.has(options.statement, 'insert')) {
queryType = 'insert';
}
else if (_.has(options.statement, 'update')) {
queryType = 'update';
}
else if (_.has(options.statement, 'del')) {
// Notice that these don't all follow exactly the same pattern!
queryType = 'delete';
}
else if (_.has(options.statement, 'count')) {
queryType = 'count';
}
else if (_.has(options.statement, 'sum')) {
queryType = 'sum';
}
else if (_.has(options.statement, 'avg')) {
queryType = 'avg';
}
else {
return done(new Error(
'This statement cannot be classified. More specifically, it does not '+
'match any recognizable "query type"; meaning it is probably not valid.\n'+
'See WLQL docs, or for additional help, visit http://sailsjs.com/support.'
));
}//>-•
// ╔═╗╔═╗╔╦╗╔═╗╦╦ ╔═╗ ┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┐┌┌┬┐
// ║ ║ ║║║║╠═╝║║ ║╣ └─┐ │ ├─┤ │ ├┤ │││├┤ │││ │
// ╚═╝╚═╝╩ ╩╩ ╩╩═╝╚═╝ └─┘ ┴ ┴ ┴ ┴ └─┘┴ ┴└─┘┘└┘ ┴
// Attempt to build a native query from the provided WLQL statement.
// (Note that `compiledStatementReport.meta` is combined with the provided `meta` option,
// if any, and then passed in when calling `sendNativeQuery`. In the event of a conflicting
// key, the `meta` from the compileStatement() call takes precedence.)
//
// > More info:
// > • https://github.com/treelinehq/waterline-query-docs
var compiledStatementReport;
try {
compiledStatementReport = options.driver.compileStatement({
statement: options.statement,
meta: options.meta
}).execSync();
} catch (e) {
switch (e.code) {
case 'malformed': return done(e.output.error);
case 'notSupported': return done(e.output.error);
default: return done(e);
}
}
// ╔═╗╔═╗╔╗╔╔╦╗ ┌┐┌┌─┐┌┬┐┬┬ ┬┌─┐ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
// ╚═╗║╣ ║║║ ║║ │││├─┤ │ │└┐┌┘├┤ │─┼┐│ │├┤ ├┬┘└┬┘
// ╚═╝╚═╝╝╚╝═╩╝ ┘└┘┴ ┴ ┴ ┴ └┘ └─┘ └─┘└└─┘└─┘┴└─ ┴
// Send the compiled native query to the database.
helpSendNativeQuery({
nativeQuery: compiledStatementReport.nativeQuery,
valuesToEscape: compiledStatementReport.valuesToEscape,
driver: options.driver,
manager: options.manager, // \____one or the other__
connection: options.connection,// /¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
meta: _.extend(options.meta||{}, compiledStatementReport.meta),
}, function (err, rawResult) {
if (err) {
return done(err);
}
// ╔═╗╔═╗╦═╗╔═╗╔═╗ ┌┐┌┌─┐┌┬┐┬┬ ┬┌─┐ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬ ┬─┐┌─┐┌─┐┬ ┬┬ ┌┬┐
// ╠═╝╠═╣╠╦╝╚═╗║╣ │││├─┤ │ │└┐┌┘├┤ │─┼┐│ │├┤ ├┬┘└┬┘ ├┬┘├┤ └─┐│ ││ │
// ╩ ╩ ╩╩╚═╚═╝╚═╝ ┘└┘┴ ┴ ┴ ┴ └┘ └─┘ └─┘└└─┘└─┘┴└─ ┴ ┴└─└─┘└─┘└─┘┴─┘┴
// Parse the raw result from the native query.
//
// > More info:
// > • https://github.com/node-machine/driver-interface/blob/386b5691806164f1d429fac54b84db97a978d601/machines/parse-native-query-result.js
// > • https://github.com/treelinehq/waterline-query-docs/blob/8f0228cbb05fca72693cc2cb3747e05593b8063c/docs/results.md
var resultParsingReport;
try {
resultParsingReport = options.driver.parseNativeQueryResult({
queryType: queryType,
nativeQueryResult: rawResult
}).execSync();
} catch (e) {
return done(new Error(
'Attempting to parse the raw result from this native query resulted in an unexpected error:\n'+
'```\n'+
e.stack+'\n'+
'```\n'+
'Here is the raw result that caused the error:\n'+
util.inspect(rawResult, {depth: 5})+'\n'+
'```'
));
}//</catch> >-•
// > (Note that `resultParsingReport.meta` is ignored)
return done(undefined, resultParsingReport.result);
});//</callback from helpSendNativeQuery()>
};
// To test:
// ```
// User.getDatastore().sendStatement({ avg: 'luckyNumber', from: 'user', where: { id: 1 } }).exec(function _afterwards(){if (arguments[0]) { console.log('ERROR:', arguments[0]); return; } console.log('Ok. Result:',arguments[1]); });
// ```