Initial commit
This commit is contained in:
481
node_modules/knex/lib/dialects/oracledb/query/oracledb-querycompiler.js
generated
vendored
Executable file
481
node_modules/knex/lib/dialects/oracledb/query/oracledb-querycompiler.js
generated
vendored
Executable file
@@ -0,0 +1,481 @@
|
||||
const clone = require('lodash/clone');
|
||||
const each = require('lodash/each');
|
||||
const isEmpty = require('lodash/isEmpty');
|
||||
const isPlainObject = require('lodash/isPlainObject');
|
||||
const Oracle_Compiler = require('../../oracle/query/oracle-querycompiler');
|
||||
const ReturningHelper = require('../utils').ReturningHelper;
|
||||
const BlobHelper = require('../utils').BlobHelper;
|
||||
const { isString } = require('../../../util/is');
|
||||
const {
|
||||
columnize: columnize_,
|
||||
} = require('../../../formatter/wrappingFormatter');
|
||||
|
||||
class Oracledb_Compiler extends Oracle_Compiler {
|
||||
// Compiles an "insert" query, allowing for multiple
|
||||
// inserts using a single query statement.
|
||||
insert() {
|
||||
const self = this;
|
||||
const outBindPrep = this._prepOutbindings(
|
||||
this.single.insert,
|
||||
this.single.returning
|
||||
);
|
||||
const outBinding = outBindPrep.outBinding;
|
||||
const returning = outBindPrep.returning;
|
||||
const insertValues = outBindPrep.values;
|
||||
|
||||
if (
|
||||
Array.isArray(insertValues) &&
|
||||
insertValues.length === 1 &&
|
||||
isEmpty(insertValues[0])
|
||||
) {
|
||||
const returningFragment = this.single.returning
|
||||
? ' (' + this.formatter.wrap(this.single.returning) + ')'
|
||||
: '';
|
||||
|
||||
return this._addReturningToSqlAndConvert(
|
||||
'insert into ' +
|
||||
this.tableName +
|
||||
returningFragment +
|
||||
' values (default)',
|
||||
outBinding[0],
|
||||
this.tableName,
|
||||
returning
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isEmpty(this.single.insert) &&
|
||||
typeof this.single.insert !== 'function'
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const insertData = this._prepInsert(insertValues);
|
||||
|
||||
const sql = {};
|
||||
|
||||
if (isString(insertData)) {
|
||||
return this._addReturningToSqlAndConvert(
|
||||
'insert into ' + this.tableName + ' ' + insertData,
|
||||
outBinding[0],
|
||||
this.tableName,
|
||||
returning
|
||||
);
|
||||
}
|
||||
|
||||
if (insertData.values.length === 1) {
|
||||
return this._addReturningToSqlAndConvert(
|
||||
'insert into ' +
|
||||
this.tableName +
|
||||
' (' +
|
||||
this.formatter.columnize(insertData.columns) +
|
||||
') values (' +
|
||||
this.client.parameterize(
|
||||
insertData.values[0],
|
||||
undefined,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
) +
|
||||
')',
|
||||
outBinding[0],
|
||||
this.tableName,
|
||||
returning
|
||||
);
|
||||
}
|
||||
|
||||
const insertDefaultsOnly = insertData.columns.length === 0;
|
||||
sql.returning = returning;
|
||||
sql.sql =
|
||||
'begin ' +
|
||||
insertData.values
|
||||
.map(function (value, index) {
|
||||
const parameterizedValues = !insertDefaultsOnly
|
||||
? self.client.parameterize(
|
||||
value,
|
||||
self.client.valueForUndefined,
|
||||
self.builder,
|
||||
self.bindingsHolder
|
||||
)
|
||||
: '';
|
||||
let subSql = 'insert into ' + self.tableName;
|
||||
|
||||
if (insertDefaultsOnly) {
|
||||
// No columns given so only the default value
|
||||
subSql +=
|
||||
' (' +
|
||||
self.formatter.wrap(self.single.returning) +
|
||||
') values (default)';
|
||||
} else {
|
||||
subSql +=
|
||||
' (' +
|
||||
self.formatter.columnize(insertData.columns) +
|
||||
') values (' +
|
||||
parameterizedValues +
|
||||
')';
|
||||
}
|
||||
|
||||
let returningClause = '';
|
||||
let intoClause = '';
|
||||
// ToDo review if this code is still needed or could be dropped
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let usingClause = '';
|
||||
let outClause = '';
|
||||
|
||||
each(value, function (val) {
|
||||
if (!(val instanceof BlobHelper)) {
|
||||
usingClause += ' ?,';
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
usingClause = usingClause.slice(0, -1);
|
||||
|
||||
// Build returning and into clauses
|
||||
outBinding[index].forEach(function (ret) {
|
||||
const columnName = ret.columnName || ret;
|
||||
returningClause += self.formatter.wrap(columnName) + ',';
|
||||
intoClause += ' ?,';
|
||||
outClause += ' out ?,';
|
||||
|
||||
// Add Helpers to bindings
|
||||
if (ret instanceof BlobHelper) {
|
||||
return self.formatter.bindings.push(ret);
|
||||
}
|
||||
self.formatter.bindings.push(new ReturningHelper(columnName));
|
||||
});
|
||||
|
||||
// Strip last comma
|
||||
returningClause = returningClause.slice(0, -1);
|
||||
intoClause = intoClause.slice(0, -1);
|
||||
outClause = outClause.slice(0, -1);
|
||||
|
||||
if (returningClause && intoClause) {
|
||||
subSql += ' returning ' + returningClause + ' into' + intoClause;
|
||||
}
|
||||
|
||||
// Pre bind position because subSql is an execute immediate parameter
|
||||
// later position binding will only convert the ? params
|
||||
subSql = self.formatter.client.positionBindings(subSql);
|
||||
const parameterizedValuesWithoutDefaultAndBlob = parameterizedValues
|
||||
.replace(/DEFAULT, /g, '')
|
||||
.replace(/, DEFAULT/g, '')
|
||||
.replace('EMPTY_BLOB(), ', '')
|
||||
.replace(', EMPTY_BLOB()', '');
|
||||
return (
|
||||
"execute immediate '" +
|
||||
subSql.replace(/'/g, "''") +
|
||||
(parameterizedValuesWithoutDefaultAndBlob || value
|
||||
? "' using "
|
||||
: '') +
|
||||
parameterizedValuesWithoutDefaultAndBlob +
|
||||
(parameterizedValuesWithoutDefaultAndBlob && outClause ? ',' : '') +
|
||||
outClause +
|
||||
';'
|
||||
);
|
||||
})
|
||||
.join(' ') +
|
||||
'end;';
|
||||
|
||||
sql.outBinding = outBinding;
|
||||
if (returning[0] === '*') {
|
||||
// Generate select statement with special order by
|
||||
// to keep the order because 'in (..)' may change the order
|
||||
sql.returningSql = function () {
|
||||
return (
|
||||
'select * from ' +
|
||||
self.tableName +
|
||||
' where ROWID in (' +
|
||||
this.outBinding
|
||||
.map(function (v, i) {
|
||||
return ':' + (i + 1);
|
||||
})
|
||||
.join(', ') +
|
||||
')' +
|
||||
' order by case ROWID ' +
|
||||
this.outBinding
|
||||
.map(function (v, i) {
|
||||
return 'when CHARTOROWID(:' + (i + 1) + ') then ' + i;
|
||||
})
|
||||
.join(' ') +
|
||||
' end'
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
with() {
|
||||
// WITH RECURSIVE is a syntax error in Oracle SQL.
|
||||
// So mark all statements as non-recursive, generate the SQL, then restore.
|
||||
// This approach ensures any changes in base class with() get propagated here.
|
||||
const undoList = [];
|
||||
if (this.grouped.with) {
|
||||
for (const stmt of this.grouped.with) {
|
||||
if (stmt.recursive) {
|
||||
undoList.push(stmt);
|
||||
stmt.recursive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = super.with();
|
||||
|
||||
// Restore the recursive markings, in case this same query gets cloned and passed to other drivers.
|
||||
for (const stmt of undoList) {
|
||||
stmt.recursive = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
_addReturningToSqlAndConvert(sql, outBinding, tableName, returning) {
|
||||
const self = this;
|
||||
const res = {
|
||||
sql: sql,
|
||||
};
|
||||
|
||||
if (!outBinding) {
|
||||
return res;
|
||||
}
|
||||
const returningValues = Array.isArray(outBinding)
|
||||
? outBinding
|
||||
: [outBinding];
|
||||
let returningClause = '';
|
||||
let intoClause = '';
|
||||
// Build returning and into clauses
|
||||
returningValues.forEach(function (ret) {
|
||||
const columnName = ret.columnName || ret;
|
||||
returningClause += self.formatter.wrap(columnName) + ',';
|
||||
intoClause += '?,';
|
||||
|
||||
// Add Helpers to bindings
|
||||
if (ret instanceof BlobHelper) {
|
||||
return self.formatter.bindings.push(ret);
|
||||
}
|
||||
self.formatter.bindings.push(new ReturningHelper(columnName));
|
||||
});
|
||||
res.sql = sql;
|
||||
|
||||
// Strip last comma
|
||||
returningClause = returningClause.slice(0, -1);
|
||||
intoClause = intoClause.slice(0, -1);
|
||||
if (returningClause && intoClause) {
|
||||
res.sql += ' returning ' + returningClause + ' into ' + intoClause;
|
||||
}
|
||||
res.outBinding = [outBinding];
|
||||
if (returning[0] === '*') {
|
||||
res.returningSql = function () {
|
||||
return 'select * from ' + self.tableName + ' where ROWID = :1';
|
||||
};
|
||||
}
|
||||
res.returning = returning;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
_prepOutbindings(paramValues, paramReturning) {
|
||||
const result = {};
|
||||
let params = paramValues || [];
|
||||
let returning = paramReturning || [];
|
||||
if (!Array.isArray(params) && isPlainObject(paramValues)) {
|
||||
params = [params];
|
||||
}
|
||||
// Always wrap returning argument in array
|
||||
if (returning && !Array.isArray(returning)) {
|
||||
returning = [returning];
|
||||
}
|
||||
|
||||
const outBinding = [];
|
||||
// Handle Buffer value as Blob
|
||||
each(params, function (values, index) {
|
||||
if (returning[0] === '*') {
|
||||
outBinding[index] = ['ROWID'];
|
||||
} else {
|
||||
outBinding[index] = clone(returning);
|
||||
}
|
||||
each(values, function (value, key) {
|
||||
if (value instanceof Buffer) {
|
||||
values[key] = new BlobHelper(key, value);
|
||||
|
||||
// Delete blob duplicate in returning
|
||||
const blobIndex = outBinding[index].indexOf(key);
|
||||
if (blobIndex >= 0) {
|
||||
outBinding[index].splice(blobIndex, 1);
|
||||
values[key].returning = true;
|
||||
}
|
||||
outBinding[index].push(values[key]);
|
||||
}
|
||||
if (value === undefined) {
|
||||
delete params[index][key];
|
||||
}
|
||||
});
|
||||
});
|
||||
result.returning = returning;
|
||||
result.outBinding = outBinding;
|
||||
result.values = params;
|
||||
return result;
|
||||
}
|
||||
|
||||
_groupOrder(item, type) {
|
||||
return super._groupOrderNulls(item, type);
|
||||
}
|
||||
|
||||
update() {
|
||||
const self = this;
|
||||
const sql = {};
|
||||
const outBindPrep = this._prepOutbindings(
|
||||
this.single.update || this.single.counter,
|
||||
this.single.returning
|
||||
);
|
||||
const outBinding = outBindPrep.outBinding;
|
||||
const returning = outBindPrep.returning;
|
||||
|
||||
const updates = this._prepUpdate(this.single.update);
|
||||
const where = this.where();
|
||||
|
||||
let returningClause = '';
|
||||
let intoClause = '';
|
||||
|
||||
if (isEmpty(updates) && typeof this.single.update !== 'function') {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Build returning and into clauses
|
||||
outBinding.forEach(function (out) {
|
||||
out.forEach(function (ret) {
|
||||
const columnName = ret.columnName || ret;
|
||||
returningClause += self.formatter.wrap(columnName) + ',';
|
||||
intoClause += ' ?,';
|
||||
|
||||
// Add Helpers to bindings
|
||||
if (ret instanceof BlobHelper) {
|
||||
return self.formatter.bindings.push(ret);
|
||||
}
|
||||
self.formatter.bindings.push(new ReturningHelper(columnName));
|
||||
});
|
||||
});
|
||||
// Strip last comma
|
||||
returningClause = returningClause.slice(0, -1);
|
||||
intoClause = intoClause.slice(0, -1);
|
||||
|
||||
sql.outBinding = outBinding;
|
||||
sql.returning = returning;
|
||||
sql.sql =
|
||||
'update ' +
|
||||
this.tableName +
|
||||
' set ' +
|
||||
updates.join(', ') +
|
||||
(where ? ' ' + where : '');
|
||||
if (outBinding.length && !isEmpty(outBinding[0])) {
|
||||
sql.sql += ' returning ' + returningClause + ' into' + intoClause;
|
||||
}
|
||||
if (returning[0] === '*') {
|
||||
sql.returningSql = function () {
|
||||
let sql = 'select * from ' + self.tableName;
|
||||
const modifiedRowsCount = this.rowsAffected.length || this.rowsAffected;
|
||||
let returningSqlIn = ' where ROWID in (';
|
||||
let returningSqlOrderBy = ') order by case ROWID ';
|
||||
|
||||
// Needs special order by because in(...) change result order
|
||||
for (let i = 0; i < modifiedRowsCount; i++) {
|
||||
if (this.returning[0] === '*') {
|
||||
returningSqlIn += ':' + (i + 1) + ', ';
|
||||
returningSqlOrderBy +=
|
||||
'when CHARTOROWID(:' + (i + 1) + ') then ' + i + ' ';
|
||||
}
|
||||
}
|
||||
if (this.returning[0] === '*') {
|
||||
this.returning = this.returning.slice(0, -1);
|
||||
returningSqlIn = returningSqlIn.slice(0, -2);
|
||||
returningSqlOrderBy = returningSqlOrderBy.slice(0, -1);
|
||||
}
|
||||
return (sql += returningSqlIn + returningSqlOrderBy + ' end');
|
||||
};
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
_jsonPathWrap(extraction) {
|
||||
return `'${extraction.path || extraction[1]}'`;
|
||||
}
|
||||
|
||||
// Json functions
|
||||
jsonExtract(params) {
|
||||
return this._jsonExtract(
|
||||
params.singleValue ? 'json_value' : 'json_query',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
jsonSet(params) {
|
||||
return `json_transform(${columnize_(
|
||||
params.column,
|
||||
this.builder,
|
||||
this.client,
|
||||
this.bindingsHolder
|
||||
)}, set ${this.client.parameter(
|
||||
params.path,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
)} = ${this.client.parameter(
|
||||
params.value,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
)})`;
|
||||
}
|
||||
|
||||
jsonInsert(params) {
|
||||
return `json_transform(${columnize_(
|
||||
params.column,
|
||||
this.builder,
|
||||
this.client,
|
||||
this.bindingsHolder
|
||||
)}, insert ${this.client.parameter(
|
||||
params.path,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
)} = ${this.client.parameter(
|
||||
params.value,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
)})`;
|
||||
}
|
||||
|
||||
jsonRemove(params) {
|
||||
const jsonCol = `json_transform(${columnize_(
|
||||
params.column,
|
||||
this.builder,
|
||||
this.client,
|
||||
this.bindingsHolder
|
||||
)}, remove ${this.client.parameter(
|
||||
params.path,
|
||||
this.builder,
|
||||
this.bindingsHolder
|
||||
)})`;
|
||||
return params.alias
|
||||
? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
|
||||
: jsonCol;
|
||||
}
|
||||
|
||||
whereJsonPath(statement) {
|
||||
return this._whereJsonPath('json_value', statement);
|
||||
}
|
||||
|
||||
whereJsonSupersetOf(statement) {
|
||||
throw new Error(
|
||||
'Json superset where clause not actually supported by Oracle'
|
||||
);
|
||||
}
|
||||
|
||||
whereJsonSubsetOf(statement) {
|
||||
throw new Error(
|
||||
'Json subset where clause not actually supported by Oracle'
|
||||
);
|
||||
}
|
||||
|
||||
onJsonPathEquals(clause) {
|
||||
return this._onJsonPathEquals('json_value', clause);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Oracledb_Compiler;
|
||||
Reference in New Issue
Block a user