n8n-nodes-base
Version:
Base nodes of n8n
926 lines • 38.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.configureTableSchemaUpdater = void 0;
exports.mapDbType = mapDbType;
exports.getCompatibleValue = getCompatibleValue;
exports.quoteSqlIdentifier = quoteSqlIdentifier;
exports.addSortRules = addSortRules;
exports.getColumnMetaData = getColumnMetaData;
exports.prepareErrorItem = prepareErrorItem;
exports.configureQueryRunner = configureQueryRunner;
exports.getColumnMap = getColumnMap;
exports.addWhereClauses = addWhereClauses;
exports.checkItemAgainstSchema = checkItemAgainstSchema;
exports.getBindParameters = getBindParameters;
exports.getInBindParametersForSourceSelect = getInBindParametersForSourceSelect;
exports.getOnClauseFromColumns = getOnClauseFromColumns;
exports.getUpdateSetClause = getUpdateSetClause;
exports.getInsertClauseAndBinds = getInsertClauseAndBinds;
exports.getInBindParametersForExecute = getInBindParametersForExecute;
exports.getOutBindDefsForExecute = getOutBindDefsForExecute;
exports.getBindDefsForExecuteMany = getBindDefsForExecuteMany;
exports.formatItemValues = formatItemValues;
const crypto_1 = require("crypto");
const luxon_1 = require("luxon");
const n8n_workflow_1 = require("n8n-workflow");
const oracledb_1 = __importDefault(require("oracledb"));
const utilities_1 = require("../../../../utils/utilities");
const n8nTypetoDBType = {
boolean: oracledb_1.default.DB_TYPE_BOOLEAN,
date: oracledb_1.default.DATE,
dateTime: oracledb_1.default.DB_TYPE_TIMESTAMP,
json: oracledb_1.default.DB_TYPE_JSON,
number: oracledb_1.default.NUMBER,
sparse: oracledb_1.default.DB_TYPE_VECTOR,
vector: oracledb_1.default.DB_TYPE_VECTOR,
string: oracledb_1.default.STRING,
blob: oracledb_1.default.BLOB,
};
function isDateType(type) {
return /^(timestamp(\(\d+\))?( with(?: local)? time zone)?|date)$/i.test(type);
}
function mapDbType(dbType) {
const type = dbType.toLowerCase().trim();
switch (true) {
// Date & Time
case /^timestamp(\(\d+\))?$/.test(type):
return { oracledbType: oracledb_1.default.DB_TYPE_TIMESTAMP, n8nType: 'dateTime' };
case /^timestamp(\(\d+\))? with time zone$/.test(type):
return { oracledbType: oracledb_1.default.DB_TYPE_TIMESTAMP_TZ, n8nType: 'dateTime' };
case /^timestamp(\(\d+\))? with local time zone$/.test(type):
return { oracledbType: oracledb_1.default.DB_TYPE_TIMESTAMP_LTZ, n8nType: 'dateTime' };
case type === 'date':
return { oracledbType: oracledb_1.default.DATE, n8nType: 'dateTime' };
// Numbers
case type === 'number':
case /^(binary_double|binary_float|decimal|float|int|integer|smallint)$/.test(type):
return { oracledbType: oracledb_1.default.DB_TYPE_NUMBER, n8nType: 'number' };
// Strings
case /^(char|clob|long|nchar|nclob|nvarchar2|rowid|urowid|varchar|varchar2|xmltype)$/.test(type):
return { oracledbType: oracledb_1.default.STRING, n8nType: 'string' };
// Boolean
case type === 'boolean':
return { oracledbType: oracledb_1.default.DB_TYPE_BOOLEAN, n8nType: 'boolean' };
// Binary / LOB
case /^(raw|long raw)$/.test(type):
return { oracledbType: oracledb_1.default.BUFFER, n8nType: 'json' };
case type === 'blob':
return { oracledbType: oracledb_1.default.BLOB, n8nType: 'json' };
// JSON
case type === 'json':
return { oracledbType: oracledb_1.default.DB_TYPE_JSON, n8nType: 'object' };
// Vector
case /^vector(\([^)]*\))?$/.test(type):
return { oracledbType: oracledb_1.default.DB_TYPE_VECTOR, n8nType: 'array' };
// Fallback
default:
return { oracledbType: oracledb_1.default.STRING, n8nType: 'string' };
}
}
/*
* It returns the value compatible based on input
* sql types (VARCHAR2, TIMESTAMP, NUMBER..)
*/
function getCompatibleValue(type, value) {
if (value === null || value === undefined)
return value;
if (isDateType(type)) {
if (value instanceof luxon_1.DateTime) {
return value.isValid ? value.toJSDate() : null;
}
if (value instanceof Date) {
return isNaN(value.getTime()) ? null : value;
}
else if (typeof value === 'string') {
return new Date(value);
}
}
if (type === 'BOOLEAN') {
if (typeof value === 'string')
return value === 'true';
return Boolean(value);
}
if (type === 'NUMBER' && typeof value === 'string') {
return Number(value);
}
if ((type === 'BLOB' || type === 'RAW' || type === 'LONG RAW') && !Buffer.isBuffer(value)) {
// User enters an array or string(For BLOB) manually, we convert to buffer.
return Buffer.from(value);
}
return value;
}
function getExecuteOptions(options = {}) {
const execOptions = {
autoCommit: options.autoCommit,
fetchArraySize: options.fetchArraySize,
maxRows: options.maxRows,
prefetchRows: options.prefetchRows,
stmtCacheSize: options.stmtCacheSize,
keepInStmtCache: options.keepInStmtCache,
bindDefs: options.bindDefs,
};
return execOptions;
}
function getExecuteManyOptions(options = {}) {
const execOptions = {
autoCommit: options.autoCommit,
keepInStmtCache: options.keepInStmtCache,
stmtCacheSize: options.stmtCacheSize,
bindDefs: options.bindDefs,
};
return execOptions;
}
function addBindParam(colInfo, value, bindParameters, bindDefs = null) {
const obj = {};
const { type, maxSize } = colInfo;
obj.type = mapDbType(type).oracledbType;
if (bindDefs) {
if (obj.type === oracledb_1.default.STRING) {
obj.maxSize = maxSize;
}
else if (obj.type === oracledb_1.default.DB_TYPE_RAW) {
obj.maxSize = maxSize;
}
bindDefs.push(obj);
}
else {
value = getCompatibleValue(type, value);
obj.val = value;
bindParameters.push(obj);
}
}
function quoteSqlIdentifier(name) {
name = name.trim();
/*
* This regex ensures the identifier is valid.
* It allows:
* Quoted identifiers: "MyColumn"
* Unquoted identifiers without . or " inside: my_column
* Dot-separated identifiers (like schema.table.column), each part following the rules above.
*
* Invalid examples:
* my"column → contains an illegal ".
* my..column → double dot not allowed.
*/
const validateRegex = /^(?:"[^"]+"|[^".]+)(?:\.(?:"[^"]+"|[^".]+))*$/;
if (!validateRegex.test(name)) {
throw new Error(`Identifier name ${name} is not valid.`);
}
// Extract each part.
const matchRegex = /"([^"]+)"|([^".]+)/g;
const groups = [];
let match;
while ((match = matchRegex.exec(name)) !== null) {
groups.push(match[1] || match[2]); // match[1] = quoted, match[2] = unquoted
}
// Re-quote each part
const quotedParts = groups.map((g) => `"${g}"`);
return quotedParts.join('.');
}
function addSortRules(query, rules) {
if (rules.length === 0)
return query;
let orderByQuery = ' ORDER BY';
rules.forEach((rule, index) => {
const endWith = index === rules.length - 1 ? '' : ',';
const sortDirection = rule.direction === 'DESC' ? 'DESC' : 'ASC';
orderByQuery += ` ${quoteSqlIdentifier(rule.column)} ${sortDirection}${endWith}`;
});
return `${query}${orderByQuery}`;
}
async function getColumnMetaData(node, pool, schema, table, index = 0) {
let conn;
try {
conn = await pool.getConnection();
const isCDBSupported = conn.oracleServerVersion >= 1200000000;
const sql = isCDBSupported
? `
WITH constraint_info AS (
SELECT
acc.owner,
acc.table_name,
acc.column_name,
LISTAGG(ac.constraint_type, ',') WITHIN GROUP (ORDER BY ac.constraint_type) AS constraint_types
FROM all_cons_columns acc
JOIN all_constraints ac
ON acc.constraint_name = ac.constraint_name
AND acc.owner = ac.owner
GROUP BY acc.owner, acc.table_name, acc.column_name
)
SELECT
atc.COLUMN_NAME,
atc.DATA_TYPE,
atc.DATA_LENGTH,
atc.CHAR_LENGTH,
atc.DEFAULT_LENGTH,
atc.NULLABLE,
atc.IDENTITY_COLUMN,
CASE WHEN atc.DATA_DEFAULT IS NOT NULL THEN 'YES' ELSE 'NO' END AS HAS_DEFAULT,
ci.constraint_types
FROM all_tab_columns atc
LEFT JOIN constraint_info ci
ON atc.owner = ci.owner
AND atc.table_name = ci.table_name
AND atc.column_name = ci.column_name
WHERE atc.owner = :schema_name
AND atc.table_name = :table_name
ORDER BY atc.COLUMN_NAME`
: `
WITH constraint_info AS (
SELECT
acc.owner,
acc.table_name,
acc.column_name,
RTRIM(
XMLAGG(
XMLELEMENT(e, ac.constraint_type || ',')
ORDER BY ac.constraint_type
).EXTRACT('//text()').getClobVal(),
','
) AS constraint_types
FROM all_cons_columns acc
JOIN all_constraints ac
ON acc.constraint_name = ac.constraint_name
AND acc.owner = ac.owner
GROUP BY acc.owner, acc.table_name, acc.column_name
)
SELECT
atc.COLUMN_NAME,
atc.DATA_TYPE,
atc.DATA_LENGTH,
atc.CHAR_LENGTH,
atc.DEFAULT_LENGTH,
atc.NULLABLE,
CASE WHEN atc.DATA_DEFAULT IS NOT NULL THEN 'YES' ELSE 'NO' END AS HAS_DEFAULT,
ci.constraint_types
FROM all_tab_columns atc
LEFT JOIN constraint_info ci
ON atc.owner = ci.owner
AND atc.table_name = ci.table_name
AND atc.column_name = ci.column_name
WHERE atc.owner = :schema_name
AND atc.table_name = :table_name
ORDER BY atc.COLUMN_NAME`;
const result = await conn.execute(sql, { table_name: table, schema_name: schema }, { outFormat: oracledb_1.default.OUT_FORMAT_OBJECT });
const rows = result.rows ?? [];
if (rows.length === 0) {
throw new n8n_workflow_1.NodeOperationError(node, 'Schema Information does not exist for selected table', {
itemIndex: index,
});
}
return rows.map((row) => ({
columnName: row.COLUMN_NAME,
isGenerated: isCDBSupported && row.IDENTITY_COLUMN === 'YES' ? 'ALWAYS' : 'NEVER',
columnDefault: row.HAS_DEFAULT,
dataType: row.DATA_TYPE,
isNullable: row.NULLABLE === 'Y',
maxSize: row.DATA_LENGTH,
}));
}
finally {
if (conn) {
await conn.close();
}
}
}
function prepareErrorItem(items, error, index) {
return {
json: { message: error.message, item: { ...items[index].json }, error: { ...error } },
pairedItem: { item: index },
};
}
function parseOracleError(node, error, itemIndex = 0) {
return new n8n_workflow_1.NodeOperationError(node, error, {
message: error.message,
itemIndex,
});
}
/*
* The outbinds returned in result.outBinds will be returned as batch of rows in case
* of executeMany and a single row for execute.
* This function would return the rows of output columns in an array for both cases.
*/
function normalizeOutBinds(outBinds, stmtBatching, outputColumns) {
const rows = [];
if (!Array.isArray(outBinds)) {
// For execute operation mode, we get outBinds as object and
// array for other insert, update, upsert operations.
const row = {};
for (const [key, val] of Object.entries(outBinds)) {
// If val is expected to be an array, safely extract the first element
row[key] = Array.isArray(val) ? val[0] : val;
}
rows.push(row);
return rows;
}
// executeMany case outBinds-> [ [[col1Row1Val], [col2Row1Val]], [[col1Row2Val], [col2Row2Val]], ...]
if (stmtBatching === 'single') {
for (const batch of outBinds) {
rows.push(Object.fromEntries(outputColumns.map((col, i) => [col, batch[i][0]])));
}
return rows;
}
// execute case outBinds-> [[col1Row1Val], [col2Row1Val]]
const row = {};
for (let i = 0; i < outBinds.length; i++) {
row[`${outputColumns[i]}`] = outBinds[i][0];
}
rows.push(row);
return rows;
}
function _getResponseForOutbinds(results, stmtBatching, outputColumns = [], returnData = []) {
if (results.outBinds) {
const normalizedRows = normalizeOutBinds(results.outBinds, stmtBatching, outputColumns);
for (let j = 0; j < normalizedRows.length; j++) {
const executionData = this.helpers.constructExecutionMetaData((0, utilities_1.wrapData)(normalizedRows[j]), {
itemData: { item: j },
});
if (executionData) {
returnData.push(...executionData);
}
}
}
}
/*
* Returns true, if update sql is tried and either row does not
* exist or could not find row with matching values.
*/
function doesRowExist(query, results) {
if (/^\s*UPDATE\b/i.test(query) && results.rowsAffected === 0) {
throw new Error("The row you are trying to update doesn't exist");
}
}
function configureQueryRunner(node, continueOnFail, pool) {
return async (queries, items, options) => {
let returnData = [];
const emptyRowData = { success: true };
let execOptions = {};
const defaultBatching = options.operation === 'insert' ||
options.operation === 'update' ||
options.operation === 'upsert'
? 'single'
: 'independently';
const stmtBatching = options.stmtBatching || defaultBatching;
if (stmtBatching === 'transaction' || stmtBatching === 'independently') {
// setup fetch Handler for specific types.
const executeFetchHandler = function (metaData) {
if (metaData.dbType &&
[
oracledb_1.default.DB_TYPE_DATE,
oracledb_1.default.DB_TYPE_TIMESTAMP_TZ,
oracledb_1.default.DB_TYPE_TIMESTAMP_LTZ,
].includes(metaData.dbType)) {
return {
converter: (val) => {
if (!(val instanceof Date))
return val;
return val.toISOString();
},
};
}
if (metaData.dbType === oracledb_1.default.CLOB) {
return { type: oracledb_1.default.STRING };
}
if (metaData.dbType === oracledb_1.default.DB_TYPE_BLOB) {
return { type: oracledb_1.default.BUFFER };
}
if (options.largeNumbersOutputAsString) {
if (metaData.dbType === oracledb_1.default.NUMBER) {
return { type: oracledb_1.default.STRING };
}
}
if (metaData.dbType === oracledb_1.default.DB_TYPE_VECTOR) {
const myConverter = (v) => {
if (v !== null) {
return Array.from(v);
}
return v;
};
return { converter: myConverter };
}
return undefined;
};
execOptions = getExecuteOptions(options);
execOptions.outFormat = oracledb_1.default.OUT_FORMAT_OBJECT; // used for execute operation.
execOptions.fetchTypeHandler = executeFetchHandler;
}
if (stmtBatching === 'single' && queries[0].executeManyValues) {
const connection = await pool.getConnection();
try {
execOptions = getExecuteManyOptions(options);
if (continueOnFail) {
execOptions.batchErrors = true;
}
const results = await connection.executeMany(queries[0].query, queries[0].executeManyValues, execOptions);
doesRowExist(queries[0].query, results);
if (results.batchErrors) {
// If there are batch errors, we just show them but not the
// partial outbinds.
const batchErrors = results.batchErrors;
for (let i = 0; i < batchErrors.length; i++) {
const failedIndex = batchErrors[i].offset;
const error = parseOracleError(node, batchErrors[i], failedIndex);
const pairedItem = { item: failedIndex };
returnData.push({ json: { message: error.message }, pairedItem });
}
}
else {
_getResponseForOutbinds.call(this, results, stmtBatching, queries[0].outputColumns, returnData);
}
if (!returnData.length) {
// returning Clause is not given.
const pairedItem = (0, utilities_1.generatePairedItemData)(queries.length);
returnData = [{ json: emptyRowData, pairedItem }];
}
}
catch (caughtError) {
const error = parseOracleError(node, caughtError);
if (!continueOnFail)
throw error;
return [{ json: { message: error.message, error } }];
}
finally {
if (connection) {
await connection.close();
}
}
}
else if (stmtBatching === 'transaction') {
execOptions.autoCommit = false; // for transaction mode forcefully overwrite it.
const connection = await pool.getConnection();
try {
for (let i = 0; i < queries.length; i++) {
try {
const { query, values, outputColumns } = queries[i];
let transactionResults;
if (values) {
transactionResults = await connection.execute(query, values, execOptions);
}
else {
transactionResults = await connection.execute(query, [], execOptions);
}
doesRowExist(query, transactionResults);
const resultOutBinds = [];
_getResponseForOutbinds.call(this, transactionResults, stmtBatching, outputColumns, resultOutBinds);
if (!resultOutBinds.length) {
let rowData = transactionResults.rows ?? [];
if (!rowData.length) {
rowData = [emptyRowData];
}
const executionData = this.helpers.constructExecutionMetaData((0, utilities_1.wrapData)(rowData), { itemData: { item: i } });
returnData.push.apply(returnData, executionData);
}
else {
returnData.push.apply(returnData, resultOutBinds);
}
}
catch (caughtError) {
const error = parseOracleError(node, caughtError, i);
if (!continueOnFail)
throw error;
returnData.push(prepareErrorItem(items, error, i));
// The rollback happens automatically, so just return.
return returnData;
}
}
// All succeeded, commit
await connection.commit();
}
finally {
await connection.close();
}
}
else if (stmtBatching === 'independently') {
const connection = await pool.getConnection();
try {
for (let i = 0; i < queries.length; i++) {
try {
const { query, values, outputColumns } = queries[i];
let taskResults;
if (values) {
taskResults = await connection.execute(query, values, execOptions);
}
else {
taskResults = await connection.execute(query, [], execOptions);
}
doesRowExist(query, taskResults);
const resultOutBinds = [];
_getResponseForOutbinds.call(this, taskResults, stmtBatching, outputColumns, resultOutBinds);
if (!resultOutBinds.length) {
// select query or no returning clause in DML
let rowData = taskResults.rows ?? [];
if (!rowData.length) {
rowData = [emptyRowData];
}
const executionData = this.helpers.constructExecutionMetaData((0, utilities_1.wrapData)(rowData), { itemData: { item: i } });
returnData.push.apply(returnData, executionData);
}
else {
returnData.push.apply(returnData, resultOutBinds);
}
}
catch (caughtError) {
const error = parseOracleError(node, caughtError, i);
if (!continueOnFail)
throw error;
returnData.push(prepareErrorItem(items, error, i));
}
}
}
finally {
await connection.close();
}
}
return returnData;
};
}
// Returns a function which returns updated table schema, if the schema
// or table name has changed.
const configureTableSchemaUpdater = (node, initialSchema, initialTable) => {
let currentSchema = initialSchema;
let currentTable = initialTable;
return async (pool, tableSchema, schema, table, index = 0) => {
if (currentSchema !== schema || currentTable !== table) {
currentSchema = schema;
currentTable = table;
tableSchema = await getColumnMetaData(node, pool, schema, table, index);
}
return tableSchema;
};
};
exports.configureTableSchemaUpdater = configureTableSchemaUpdater;
function getColumnMap(tableSchema) {
const columnMetaDataObject = tableSchema.reduce((acc, { columnName, dataType, isNullable, maxSize }) => {
acc[columnName] = {
type: dataType.toUpperCase(),
nullable: isNullable,
maxSize,
};
return acc;
}, {});
return columnMetaDataObject;
}
function addWhereClauses(query, clauses, combineConditions, schema, isExecuteMany = false) {
if (clauses.length === 0)
return [query, []];
const combineWith = combineConditions === 'OR' ? 'OR' : 'AND';
let whereQuery = ' WHERE';
const bindParameters = [];
const bindDefs = isExecuteMany ? [] : null;
let posIndex = 0;
clauses.forEach((clause, index) => {
if (clause.condition === 'equal') {
clause.condition = '=';
}
// The condition value is json type, so convert to required type only
// if fixed expression is used instead of n8n expressions.
if (typeof clause.value === 'string') {
try {
clause.value = JSON.parse(clause.value); // "2" → 2 (number)
}
catch {
// if it wasn't valid JSON, keep original string
}
}
const quotedColumnName = quoteSqlIdentifier(clause.column); // escape column name
let conditionExpr;
if (clause.condition === 'IS NULL' || clause.condition === 'IS NOT NULL') {
conditionExpr = `${quotedColumnName} ${clause.condition}`;
}
else {
conditionExpr = `${quotedColumnName} ${clause.condition} :${posIndex++}`;
addBindParam(schema[clause.column], clause.value, bindParameters, bindDefs);
}
whereQuery += (index > 0 ? ` ${combineWith}` : '') + ` ${conditionExpr}`;
});
return [`${query}${whereQuery}`, bindDefs ?? bindParameters];
}
/*
Verifies all provided keys (item fields) exist in the table schema.
Ensures that non-nullable columns aren't being given null values.
*/
function checkItemAgainstSchema(node, columns, schema, item, index = 0) {
for (const key of columns) {
const columnDef = schema[key];
if (!columnDef) {
throw new n8n_workflow_1.NodeOperationError(node, `Column '${key}' does not exist in selected table`, {
itemIndex: index,
});
}
if (item[key] === null && !columnDef.nullable) {
throw new n8n_workflow_1.NodeOperationError(node, `Column '${key}' is not nullable`, {
itemIndex: index,
});
}
}
}
/*
* Given a query with bindname which accepts string(comma separated values) for IN clause, It
* generates multiple unique bindnames separated by comma. This bindname should be only used in IN clause.
*
* Ex: For input: SELECT * from dual where DUMMY in :param1
* It generates
* "SELECT * from dual where DUMMY in (:param1f8b29e29_edef_454a_b003_18fa1debff55,:param1bc8a5a46_5bfd_4ddb_851e_2ac4c243a26b)"
*
* The logic is taken from here
* https://github.com/jgriffin1/n8n-nodes-oracle-database-parameterization
*
*/
function generateBindVariablesList(item, bindParameters, query) {
if (item.datatype !== 'string') {
throw new n8n_workflow_1.UserError(`Unsupported datatype '${item.datatype}' for IN clause expansion. Only 'string' of comma-separated values is allowed. `);
}
const valList = item.valueString.split(',');
// Escape bind name for regex safely
const escapedName = item.name.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
let generatedSqlString = '(';
for (let i = 0; i < valList.length; i++) {
// replace parameters with Dashes to underscore
const uniqueId = (0, crypto_1.randomUUID)().replace(/-/g, '_');
const newParamName = `${item.name}${uniqueId}`;
bindParameters[newParamName] = {
type: mapDbType(item.datatype).oracledbType,
val: getCompatibleValue(item.datatype, valList[i]),
};
generatedSqlString += `:${newParamName},`;
}
// replace :bindname
const regex = new RegExp(`:${escapedName}(?![A-Za-z0-9_$#])`, 'g');
generatedSqlString = generatedSqlString.slice(0, -1) + ')'; //replace trailing comma with closing parenthesis.
return query.replace(regex, generatedSqlString);
}
function isSerializedBuffer(val) {
return (typeof val === 'object' &&
val !== null &&
'type' in val &&
'data' in val &&
val.type === 'Buffer' &&
Array.isArray(val.data));
}
function getBindParameters(query, parameterList) {
const bindParameters = {};
for (const item of parameterList) {
if (!item.parseInStatement) {
let bindVal = null;
const type = item.datatype;
switch (type) {
case 'number':
bindVal = item.valueNumber;
break;
case 'string':
bindVal = item.valueString;
break;
case 'boolean':
bindVal = item.valueBoolean;
break;
case 'blob':
bindVal = item.valueBlob;
// Allow null or undefined to represent SQL NULL BLOB values
if (bindVal === null) {
break;
}
if (Buffer.isBuffer(bindVal)) {
break;
}
// Serialized form: { type: 'Buffer', data: [...] }
if (isSerializedBuffer(bindVal)) {
bindVal = Buffer.from(bindVal.data);
break;
}
throw new n8n_workflow_1.UserError('BLOB data must be a valid Buffer or \'{ type: "Buffer", data: [...] }\'');
case 'date': {
const val = item.valueDate;
if (typeof val === 'string') {
bindVal = new Date(val); // string → Date
}
else if (val instanceof Date) {
bindVal = val; // already a Date
}
else {
// Luxon DateTime
if (val instanceof luxon_1.DateTime) {
bindVal = val.isValid ? val.toJSDate() : null;
}
}
break;
}
case 'sparse': {
const val = item.valueSparse;
let indices = val.indices;
let values = val.values;
const dims = val.dimensions;
if (typeof indices === 'string') {
try {
indices = JSON.parse(indices);
}
catch (caughtError) {
throw new n8n_workflow_1.UserError(`Indices field must be a valid JSON array or use an expression like {{ [1, 2, 3] }}, Err:${caughtError}`);
}
}
if (typeof values === 'string') {
try {
values = JSON.parse(values);
}
catch (caughtError) {
throw new n8n_workflow_1.UserError(`Values field must be a valid JSON array or use an expression like {{ [1, 2, 3] }}, Err:${caughtError} `);
}
}
bindVal = new oracledb_1.default.SparseVector({
indices,
values,
numDimensions: dims,
});
break;
}
case 'vector':
{
const val = item.valueVector;
bindVal = val;
if (typeof val === 'string') {
try {
bindVal = JSON.parse(val);
}
catch (caughtError) {
throw new n8n_workflow_1.UserError(`Value field must be a valid JSON array or use an expression like {{ [1, 2, 3] }}, Err: ${caughtError}`);
}
}
}
break;
case 'json':
{
const val = item.valueJson;
bindVal = val;
if (typeof val === 'string') {
try {
bindVal = JSON.parse(val);
}
catch {
throw new n8n_workflow_1.UserError('Value must be a valid JSON ');
}
}
}
break;
default:
throw new n8n_workflow_1.UserError(`Unsupported Bind type: ${type}`);
}
const dir = item.bindDirection === 'in'
? oracledb_1.default.BIND_IN
: item.bindDirection === 'out'
? oracledb_1.default.BIND_OUT
: oracledb_1.default.BIND_INOUT;
bindParameters[item.name] = {
type: n8nTypetoDBType[item.datatype],
val: bindVal,
dir,
};
}
else {
query = generateBindVariablesList(item, bindParameters, query);
}
}
return { updatedQuery: query, bindParameters };
}
function getInBindParametersForSourceSelect(inputColumns, schema, valData, bindParameters, bindDefs, posIndex = 0) {
const quotedColsArray = [];
let index = posIndex;
for (const col of inputColumns) {
const quotedCol = quoteSqlIdentifier(col);
quotedColsArray.push(`:${index++} as ${quotedCol}`);
addBindParam(schema[col], valData[col], bindParameters, bindDefs);
}
return [quotedColsArray.join(','), index];
}
function getOnClauseFromColumns(columns) {
return columns
.map((col) => {
const quoted = quoteSqlIdentifier(col);
return `t.${quoted} = s.${quoted}`;
})
.join(' AND ');
}
function getUpdateSetClause(updateCols, schema, valData, bindParameters, bindDefs, posIndex) {
const setClauses = [];
let index = posIndex;
for (const col of updateCols) {
const quoted = quoteSqlIdentifier(col);
setClauses.push(`t.${quoted} = :${index}`);
addBindParam(schema[col], valData[col], bindParameters, bindDefs);
index++;
}
return [setClauses.join(', '), index];
}
function getInsertClauseAndBinds(insertCols, schema, valData, bindParameters, bindDefs, posIndex) {
const quotedCols = [];
const placeholders = [];
let index = posIndex;
for (const col of insertCols) {
const quoted = quoteSqlIdentifier(col);
quotedCols.push(quoted);
placeholders.push(`:${index}`);
addBindParam(schema[col], valData[col], bindParameters, bindDefs);
index++;
}
return [quotedCols.join(', '), placeholders.join(', '), index];
}
function getInBindParametersForExecute(inputColumns, schema, valData, operation, bindParameters, posIndex = 0) {
const quotedColsArray = [];
const replacementsArray = [];
let index = posIndex;
for (const col of inputColumns) {
const quotedCol = quoteSqlIdentifier(col);
if (operation === 'insert') {
quotedColsArray.push(quotedCol);
replacementsArray.push(`:${index++}`);
}
else if (operation === 'update') {
quotedColsArray.push(`${quotedCol}=:${index++}`);
}
addBindParam(schema[col], valData[col], bindParameters);
}
const replacements = replacementsArray.join(',');
return [quotedColsArray, replacements, index];
}
function getInBindDefsForExecuteMany(query, columnMetaDataObject, columns, bindInfo, posIndex = 0, operation = 'insert') {
const quotedColsArray = [];
const replacementsArray = [];
let resultIndex = posIndex;
for (const col of columns) {
const quotedCol = quoteSqlIdentifier(col);
if (operation === 'update' || operation === 'updatewhere') {
quotedColsArray.push(`${quotedCol}=:${resultIndex++}`);
}
else {
// insert case.
quotedColsArray.push(quotedCol);
replacementsArray.push(`:${resultIndex++}`);
}
addBindParam(columnMetaDataObject[col], null, [], bindInfo);
}
const quotedCols = quotedColsArray.join(operation === 'updatewhere' ? ' AND ' : ',');
switch (operation) {
case 'insert':
query += ` (${quotedCols}) VALUES (${replacementsArray.join(',')})`;
break;
case 'update':
query += ` SET ${quotedCols}`;
break;
case 'updatewhere':
query += ` WHERE ${quotedCols}`;
break;
}
return [query, resultIndex];
}
function getOutBindDefsForExecute(query, columnMetaDataObject, outputColumns, bindInfo, bindIndex = 0) {
let returningClause = 'RETURNING ';
let intoClause = 'INTO ';
let posIndex = bindIndex;
for (const [i, col] of outputColumns.entries()) {
if (i > 0) {
returningClause += ', ';
intoClause += ', ';
}
const obj = {};
obj.type = mapDbType(columnMetaDataObject[col].type).oracledbType;
obj.dir = oracledb_1.default.BIND_OUT;
if (obj.type === oracledb_1.default.STRING) {
obj.maxSize = columnMetaDataObject[col].maxSize;
}
else if (obj.type === oracledb_1.default.DB_TYPE_RAW) {
obj.maxSize = columnMetaDataObject[col].maxSize;
}
bindInfo.push(obj);
returningClause += `${quoteSqlIdentifier(col)}`;
intoClause += `:${posIndex++}`;
}
query = `${query} ${returningClause} ${intoClause}`;
return query;
}
function getBindDefsForExecuteMany(node, query, columnMetaDataObject, inputColumns, outputColumns, item, options, operation = 'insert', columnsToMatchOn = []) {
checkItemAgainstSchema(node, inputColumns, columnMetaDataObject, item);
let posIndex = 0;
const bindDefs = [];
[query, posIndex] = getInBindDefsForExecuteMany(query, columnMetaDataObject, inputColumns, bindDefs, posIndex, operation);
if (columnsToMatchOn.length) {
[query, posIndex] = getInBindDefsForExecuteMany(query, columnMetaDataObject, columnsToMatchOn, bindDefs, posIndex, 'updatewhere');
}
if (outputColumns.length > 0) {
const updatedQuery = getOutBindDefsForExecute(query, columnMetaDataObject, outputColumns, bindDefs, posIndex);
query = updatedQuery;
}
options.bindDefs = bindDefs;
return query;
}
// It will convert the n8n values to compatible bind values.
function formatItemValues(item, col) {
const result = [];
for (const key of Object.keys(item)) {
const type = col[key].type;
const value = getCompatibleValue(type, item[key]);
result.push(value);
}
return result;
}
//# sourceMappingURL=utils.js.map