You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

480 lines
16 KiB

"use strict";
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
__export(exports, {
OracleQuery: () => OracleQuery
});
const AbstractQuery = require("../abstract/query");
const SequelizeErrors = require("../../errors");
const parserStore = require("../parserStore")("oracle");
const _ = require("lodash");
const Utils = require("../../utils");
const { logger } = require("../../utils/logger");
const debug = logger.debugContext("sql:oracle");
class OracleQuery extends AbstractQuery {
constructor(connection, sequelize, options) {
super(connection, sequelize, options);
this.options = _.extend({
logging: console.log,
plain: false,
raw: false
}, options || {});
this.checkLoggingOption();
this.outFormat = options.outFormat || this.sequelize.connectionManager.lib.OBJECT;
}
getInsertIdField() {
return "id";
}
getExecOptions() {
const execOpts = { outFormat: this.outFormat, autoCommit: this.autoCommit };
const oracledb = this.sequelize.connectionManager.lib;
if (this.isSelectQuery() && this.model) {
const fInfo = {};
const keys = Object.keys(this.model.tableAttributes);
for (const key of keys) {
const keyValue = this.model.tableAttributes[key];
if (keyValue.type.key === "DECIMAL") {
fInfo[key] = { type: oracledb.STRING };
}
if (keyValue.type.key === "BIGINT") {
fInfo[key] = { type: oracledb.STRING };
}
}
if (fInfo) {
execOpts.fetchInfo = fInfo;
}
}
return execOpts;
}
async run(sql, parameters) {
const oracledb = this.sequelize.connectionManager.lib;
const complete = this._logQuery(sql, debug, parameters);
const outParameters = [];
const bindParameters = [];
const bindDef = [];
if (!sql.match(/END;$/)) {
this.sql = sql.replace(/; *$/, "");
} else {
this.sql = sql;
}
if (this.options.outBindAttributes && (Array.isArray(parameters) || _.isPlainObject(parameters))) {
outParameters.push(...Object.values(this.options.outBindAttributes));
if (this.isUpsertQuery()) {
outParameters.push({ dir: oracledb.BIND_OUT });
}
}
this.bindParameters = outParameters;
if (Array.isArray(parameters) || _.isPlainObject(parameters)) {
if (this.options.executeMany) {
bindDef.push(...Object.values(this.options.inbindAttributes));
bindDef.push(...outParameters);
this.bindParameters = parameters;
} else {
Object.values(parameters).forEach((value) => {
bindParameters.push(value);
});
bindParameters.push(...outParameters);
Object.assign(this.bindParameters, bindParameters);
}
}
if (this.sql.startsWith("BEGIN TRANSACTION")) {
this.autocommit = false;
return Promise.resolve();
}
if (this.sql.startsWith("SET AUTOCOMMIT ON")) {
this.autocommit = true;
return Promise.resolve();
}
if (this.sql.startsWith("SET AUTOCOMMIT OFF")) {
this.autocommit = false;
return Promise.resolve();
}
if (this.sql.startsWith("DECLARE x NUMBER")) {
if (this.autoCommit === void 0) {
if (this.connection.uuid) {
this.autoCommit = false;
} else {
this.autoCommit = true;
}
}
try {
await this.connection.execute(this.sql, this.bindParameters, { autoCommit: this.autoCommit });
return Object.create(null);
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
if (this.sql.startsWith("BEGIN")) {
if (this.autoCommit === void 0) {
if (this.connection.uuid) {
this.autoCommit = false;
} else {
this.autoCommit = true;
}
}
try {
const result = await this.connection.execute(this.sql, this.bindParameters, {
outFormat: this.outFormat,
autoCommit: this.autoCommit
});
if (!Array.isArray(result.outBinds)) {
return [result.outBinds];
}
return result.outBinds;
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
if (this.sql.startsWith("COMMIT TRANSACTION")) {
try {
await this.connection.commit();
return Object.create(null);
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
if (this.sql.startsWith("ROLLBACK TRANSACTION")) {
try {
await this.connection.rollback();
return Object.create(null);
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
if (this.sql.startsWith("SET TRANSACTION")) {
try {
await this.connection.execute(this.sql, [], { autoCommit: false });
return Object.create(null);
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
if (this.autoCommit === void 0) {
if (this.connection.uuid) {
this.autoCommit = false;
} else {
this.autoCommit = true;
}
}
if ("inputParameters" in this.options && this.options.inputParameters !== null) {
Object.assign(this.bindParameters, this.options.inputParameters);
}
const execOpts = this.getExecOptions();
if (this.options.executeMany && bindDef.length > 0) {
execOpts.bindDefs = bindDef;
}
const executePromise = this.options.executeMany ? this.connection.executeMany(this.sql, this.bindParameters, execOpts) : this.connection.execute(this.sql, this.bindParameters, execOpts);
try {
const result = await executePromise;
return this.formatResults(result);
} catch (error) {
throw this.formatError(error);
} finally {
complete();
}
}
static formatBindParameters(sql, values, dialect) {
const replacementFunc = (match, key, values2) => {
if (values2[key] !== void 0) {
return `:${key}`;
}
return void 0;
};
sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0];
return [sql, values];
}
_getAttributeMap(attrsMap, rawAttributes) {
attrsMap = Object.assign(attrsMap, _.reduce(rawAttributes, (mp, _2, key) => {
const catalogKey = this.sequelize.queryInterface.queryGenerator.getCatalogName(key);
mp[catalogKey] = key;
return mp;
}, {}));
}
_processRows(rows) {
let result = rows;
let attrsMap = {};
if (this.sequelize.options.quoteIdentifiers === false) {
attrsMap = _.reduce(this.options.attributes, (mp, v) => {
if (typeof v === "object") {
v = v[1];
}
const catalogv = this.sequelize.queryInterface.queryGenerator.getCatalogName(v);
mp[catalogv] = v;
return mp;
}, {});
if (this.model) {
this._getAttributeMap(attrsMap, this.model.rawAttributes);
}
if (this.options.aliasesMapping) {
const obj = Object.fromEntries(this.options.aliasesMapping);
rows = rows.map((row) => _.toPairs(row).reduce((acc, [key, value]) => {
const mapping = Object.values(obj).find((element) => {
const catalogElement = this.sequelize.queryInterface.queryGenerator.getCatalogName(element);
return catalogElement === key;
});
if (mapping)
acc[mapping || key] = value;
return acc;
}, {}));
}
result = rows.map((row) => {
return _.mapKeys(row, (value, key) => {
const targetAttr = attrsMap[key];
if (typeof targetAttr === "string" && targetAttr !== key) {
return targetAttr;
}
return key;
});
});
}
if (this.model) {
result = result.map((row) => {
return _.mapValues(row, (value, key) => {
if (this.model.rawAttributes[key] && this.model.rawAttributes[key].type) {
let typeid = this.model.rawAttributes[key].type.toLocaleString();
if (this.model.rawAttributes[key].type.key === "JSON") {
value = JSON.parse(value);
}
if (typeid.indexOf("(") > -1 && this.model.rawAttributes[key].type.key !== "BOOLEAN") {
typeid = typeid.substr(0, typeid.indexOf("("));
}
const parse = parserStore.get(typeid);
if (value !== null & !!parse) {
value = parse(value);
}
}
return value;
});
});
}
return result;
}
formatResults(data) {
let result = this.instance;
if (this.isInsertQuery(data)) {
let insertData;
if (data.outBinds) {
const keys = Object.keys(this.options.outBindAttributes);
insertData = data.outBinds;
if (this.instance) {
insertData = [insertData];
}
const res = insertData.map((row) => {
const obj = {};
row.forEach((element, index) => {
obj[keys[index]] = element[0];
});
return obj;
});
insertData = res;
if (!this.instance) {
result = res;
}
}
this.handleInsertQuery(insertData);
return [result, data.rowsAffected];
}
if (this.isShowTablesQuery()) {
result = this.handleShowTablesQuery(data.rows);
} else if (this.isDescribeQuery()) {
result = {};
const table = Object.keys(this.sequelize.models);
const modelAttributes = {};
if (this.sequelize.models && table.length > 0) {
this._getAttributeMap(modelAttributes, this.sequelize.models[table[0]].rawAttributes);
}
data.rows.forEach((_result) => {
if (_result.Default) {
_result.Default = _result.Default.replace("('", "").replace("')", "").replace(/'/g, "");
}
if (!(modelAttributes[_result.COLUMN_NAME] in result)) {
let key = modelAttributes[_result.COLUMN_NAME];
if (!key) {
key = _result.COLUMN_NAME;
}
result[key] = {
type: _result.DATA_TYPE.toUpperCase(),
allowNull: _result.NULLABLE === "N" ? false : true,
defaultValue: void 0,
primaryKey: _result.CONSTRAINT_TYPE === "P"
};
}
});
} else if (this.isShowIndexesQuery()) {
result = this.handleShowIndexesQuery(data.rows);
} else if (this.isSelectQuery()) {
const rows = data.rows;
const result2 = this._processRows(rows);
return this.handleSelectQuery(result2);
} else if (this.isCallQuery()) {
result = data.rows[0];
} else if (this.isUpdateQuery()) {
result = [result, data.rowsAffected];
} else if (this.isBulkUpdateQuery()) {
result = data.rowsAffected;
} else if (this.isBulkDeleteQuery()) {
result = data.rowsAffected;
} else if (this.isVersionQuery()) {
const version = data.rows[0].VERSION_FULL;
if (version) {
const versions = version.split(".");
result = `${versions[0]}.${versions[1]}.${versions[2]}`;
} else {
result = "0.0.0";
}
} else if (this.isForeignKeysQuery()) {
result = data.rows;
} else if (this.isUpsertQuery()) {
data = data.outBinds;
const keys = Object.keys(this.options.outBindAttributes);
const obj = {};
for (const k in keys) {
obj[keys[k]] = data[k];
}
obj.isUpdate = data[data.length - 1];
data = obj;
result = [{ isNewRecord: data.isUpdate, value: data }, data.isUpdate == 0];
} else if (this.isShowConstraintsQuery()) {
result = this.handleShowConstraintsQuery(data);
} else if (this.isRawQuery()) {
if (data && data.rows) {
return [data.rows, data.metaData];
}
return [data, data];
}
return result;
}
handleShowConstraintsQuery(data) {
return data.rows.map((result) => {
const constraint = {};
for (const key in result) {
constraint[_.camelCase(key)] = result[key].toLowerCase();
}
return constraint;
});
}
handleShowTablesQuery(results) {
return results.map((resultSet) => {
return {
tableName: resultSet.TABLE_NAME,
schema: resultSet.TABLE_SCHEMA
};
});
}
formatError(err) {
let match;
match = err.message.match(/unique constraint ([\s\S]*) violated/);
if (match && match.length > 1) {
match[1] = match[1].replace("(", "").replace(")", "").split(".")[1];
const errors = [];
let fields = [], message = "Validation error", uniqueKey = null;
if (this.model) {
const uniqueKeys = Object.keys(this.model.uniqueKeys);
const currKey = uniqueKeys.find((key) => {
return key.toUpperCase() === match[1].toUpperCase() || key.toUpperCase() === `"${match[1].toUpperCase()}"`;
});
if (currKey) {
uniqueKey = this.model.uniqueKeys[currKey];
fields = uniqueKey.fields;
}
if (uniqueKey && !!uniqueKey.msg) {
message = uniqueKey.msg;
}
fields.forEach((field) => {
errors.push(new SequelizeErrors.ValidationErrorItem(this.getUniqueConstraintErrorMessage(field), "unique violation", field, null));
});
}
return new SequelizeErrors.UniqueConstraintError({
message,
errors,
err,
fields
});
}
match = err.message.match(/ORA-02291/) || err.message.match(/ORA-02292/);
if (match && match.length > 0) {
return new SequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match[1],
parent: err
});
}
match = err.message.match(/ORA-02443/);
if (match && match.length > 0) {
return new SequelizeErrors.UnknownConstraintError(match[1]);
}
return new SequelizeErrors.DatabaseError(err);
}
isShowIndexesQuery() {
return this.sql.indexOf("SELECT i.index_name,i.table_name, i.column_name, u.uniqueness") > -1;
}
isSelectCountQuery() {
return this.sql.toUpperCase().indexOf("SELECT COUNT(") > -1;
}
handleShowIndexesQuery(data) {
const acc = [];
data.forEach((indexRecord) => {
if (!acc[indexRecord.INDEX_NAME]) {
acc[indexRecord.INDEX_NAME] = {
unique: indexRecord.UNIQUENESS === "UNIQUE" ? true : false,
primary: indexRecord.CONSTRAINT_TYPE === "P",
name: indexRecord.INDEX_NAME.toLowerCase(),
tableName: indexRecord.TABLE_NAME.toLowerCase(),
type: void 0
};
acc[indexRecord.INDEX_NAME].fields = [];
}
acc[indexRecord.INDEX_NAME].fields.push({
attribute: indexRecord.COLUMN_NAME,
length: void 0,
order: indexRecord.DESCEND,
collate: void 0
});
});
const returnIndexes = [];
const accKeys = Object.keys(acc);
for (const accKey of accKeys) {
const columns = {};
columns.fields = acc[accKey].fields;
if (acc[accKey].name.match(/sys_c[0-9]*/)) {
acc[accKey].name = Utils.nameIndex(columns, acc[accKey].tableName).name;
}
returnIndexes.push(acc[accKey]);
}
return returnIndexes;
}
handleInsertQuery(results, metaData) {
if (this.instance && results.length > 0) {
if ("pkReturnVal" in results[0]) {
results[0][this.model.primaryKeyAttribute] = results[0].pkReturnVal;
delete results[0].pkReturnVal;
}
const autoIncrementField = this.model.autoIncrementAttribute;
let autoIncrementFieldAlias = null, id = null;
if (Object.prototype.hasOwnProperty.call(this.model.rawAttributes, autoIncrementField) && this.model.rawAttributes[autoIncrementField].field !== void 0)
autoIncrementFieldAlias = this.model.rawAttributes[autoIncrementField].field;
id = id || results && results[0][this.getInsertIdField()];
id = id || metaData && metaData[this.getInsertIdField()];
id = id || results && results[0][autoIncrementField];
id = id || autoIncrementFieldAlias && results && results[0][autoIncrementFieldAlias];
this.instance[autoIncrementField] = id;
}
}
}
//# sourceMappingURL=query.js.map