objection
Version:
An SQL-friendly ORM for Node.js
120 lines (99 loc) • 3.64 kB
JavaScript
import QueryBuilderOperation from './QueryBuilderOperation';
import {isKnexQueryBuilder, isKnexJoinBuilder} from '../../utils/dbUtils';
import ReferenceBuilder from '../ReferenceBuilder';
let QueryBuilderBase = null;
let JoinBuilder = null;
export default class WrappingQueryBuilderOperation extends QueryBuilderOperation {
constructor(name, opt) {
super(name, opt);
this.args = null;
}
call(builder, args) {
const ret = wrapArgs(this, builder, args);
this.args = args;
return ret;
}
}
function wrapArgs(op, builder, args) {
// Preventing cyclic deps
QueryBuilderBase = QueryBuilderBase || requireQueryBuilderBase();
const skipUndefined = builder.shouldSkipUndefined();
const knex = builder.knex();
for (let i = 0, l = args.length; i < l; ++i) {
const arg = args[i];
if (arg === undefined) {
if (skipUndefined) {
return false;
} else {
throw new Error(`undefined passed as argument #${l} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`);
}
} else if (arg instanceof ReferenceBuilder) {
args[i] = knex.raw(...args[i].toRawArgs());
} else if (arg instanceof QueryBuilderBase) {
// Convert QueryBuilderBase instances into knex query builders.
args[i] = arg.build();
} else if (Array.isArray(arg)) {
if (skipUndefined) {
args[i] = withoutUndefined(arg);
} else if (includesUndefined(arg)) {
throw new Error(`undefined passed as an item in argument #${l} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`);
}
// convert reference builders to knex.raw
args[i] = args[i].map(arg => {
return arg instanceof ReferenceBuilder ? knex.raw(...arg.toRawArgs()) : arg;
});
} else if (typeof arg === 'function') {
// If an argument is a function, knex calls it with a query builder as
// first argument (and as `this` context). We wrap the query builder into
// a QueryBuilderBase instance.
args[i] = wrapFunctionArg(arg, knex);
}
}
return true;
}
function wrapFunctionArg(func, knex) {
// Preventing cyclic deps
QueryBuilderBase = QueryBuilderBase || requireQueryBuilderBase();
JoinBuilder = JoinBuilder || requireJoinBuilder();
return function wrappedKnexFunctionArg() {
if (isKnexQueryBuilder(this)) {
const knexQueryBuilder = this;
// Wrap knex query builder into a QueryBuilderBase so that we can use
// our extended query builder in nested queries.
const wrappedQueryBuilder = new QueryBuilderBase(knex);
func.call(wrappedQueryBuilder, wrappedQueryBuilder);
wrappedQueryBuilder.buildInto(knexQueryBuilder);
} else if (isKnexJoinBuilder(this)) {
const knexQueryBuilder = this;
const joinClauseBuilder = new JoinBuilder(knex);
func.call(joinClauseBuilder, joinClauseBuilder);
joinClauseBuilder.buildInto(knexQueryBuilder);
} else {
// maybe someone falls here to the original knex implementation
return func.apply(this, arguments);
}
};
}
function withoutUndefined(arr) {
const out = [];
for (let i = 0, l = arr.length; i < l; ++i) {
if (arr[i] !== undefined) {
out.push(arr[i]);
}
}
return out;
}
function includesUndefined(arr) {
for (let i = 0, l = arr.length; i < l; ++i) {
if (arr[i] === undefined) {
return true;
}
}
return false;
}
function requireQueryBuilderBase() {
return require('../QueryBuilderBase').default;
}
function requireJoinBuilder() {
return require('../JoinBuilder').default;
}