diff --git a/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdb.js b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdb.js index e02771584..9785e3873 100644 --- a/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdb.js +++ b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdb.js @@ -13,33 +13,37 @@ * HANA XS Classic Bridge for HDB API */ var database = require('db/v4/database'); -const PROCEDURE_IN_PARAMETER = 1; -const PROCEDURE_IN_OUT_PARAMETER = 2; -const PROCEDURE_OUT_PARAMETER = 4; -const SQL_TABLE_TYPE = 0; +var PROCEDURE_UTILS = require('xsk/hdb/procedureUtils'); +var HDB_UTILS = require('xsk/hdb/hdbUtils'); + exports.getConnection = function () { var dConnection = database.getConnection(); return new XscConnection(dConnection); -} +}; + exports.ProcedureResult = function () { this.$resultSets = []; -} +}; + exports.ResultSet = XscResultSet; function XscConnection(dConnection) { this.close = function () { dConnection.close(); }; + this.commit = function () { dConnection.commit(); }; + this.executeQuery = function (query) { var dPreparedStatement = dConnection.prepareStatement(query); var args = Array.prototype.slice.call(arguments, 1); setStatementParams(dPreparedStatement, args); var dResultSet = dPreparedStatement.executeQuery(); return new XscResultSet(dResultSet); - } + }; + this.executeUpdate = function (statement) { var dPreparedStatement = dConnection.prepareStatement(statement); var args = Array.prototype.slice.call(arguments, 1); @@ -50,10 +54,12 @@ function XscConnection(dConnection) { return dPreparedStatement.executeUpdate(); } }; + // Returns always null. I need to think of conditions where it returns an object this.getLastWarning = function () { return dConnection.native.getWarnings(); - } + }; + this.loadProcedure = function (schema, procedure) { function procedureCall() { // Make an array of the procedure call arguments @@ -61,48 +67,26 @@ function XscConnection(dConnection) { let procedureResult = new $.hdb.ProcedureResult; let dConnection = null; let procedureCallStatement = null; - let procedureParameters = []; + let temporaryTables = []; try { dConnection = database.getConnection(); - // Get the procedure parameters - const procedureParametersResultSet = dConnection.native.getMetaData().getProcedureColumns(null, null, procedure, "%"); - while (procedureParametersResultSet.next()) { - const parameterName = procedureParametersResultSet.getString('COLUMN_NAME'); - const parameterType = procedureParametersResultSet.getShort('COLUMN_TYPE'); - const parameterTypeName = procedureParametersResultSet.getString('TYPE_NAME'); - const parameterDataType = procedureParametersResultSet.getInt('DATA_TYPE'); - procedureParameters.push({ - parameterName: parameterName, - parameterType: parameterType, - parameterTypeName: parameterTypeName, - parameterDataType: parameterDataType, - parameterValue: null - }) - } + let procedureParameters = PROCEDURE_UTILS.getProcedureParameters(dConnection, procedure); // Process the procedure arguments and set them to the in parameters - if (procedureCallArgs.length === 1 && typeof procedureCallArgs[0] === 'object' && !Array.isArray(procedureCallArgs[0]) && procedureCallArgs[0] !== null) { - procedureCallArgs = procedureCallArgs[0]; - for (let procedureCallArg in procedureCallArgs) { - procedureParameters.forEach(procedureParameter => { - if (procedureParameter.parameterName === procedureCallArg.toUpperCase()) { - procedureParameter.parameterValue = procedureCallArgs[procedureCallArg]; - } - }) - } - } else { - for (let i = 0, argIndex = 0; i < procedureParameters.length; i++) { - if (procedureParameters[i].parameterType === PROCEDURE_IN_PARAMETER || procedureParameters[i].parameterType === PROCEDURE_IN_OUT_PARAMETER) { - procedureParameters[i].parameterValue = procedureCallArgs[argIndex]; - argIndex++; + setProcedureParameterValues(dConnection, procedureParameters, procedureCallArgs); + + let sql = `CALL "${schema}"."${procedure}" (${procedureParameters.map(e => { + if (e.isTableType && PROCEDURE_UTILS.isProcedureInParamType(e.parameterType)) { + PROCEDURE_UTILS.createTemporaryTable(dConnection, e.temporaryTableName, e.temporaryTableType); + for (let i = 0; i < e.parameterValue.length; i++) { + PROCEDURE_UTILS.insertTemporaryTableData(dConnection, e.temporaryTableName, e.parameterValue[i]); } + temporaryTables.push(e.temporaryTableName); + return e.temporaryTableName; } - } - // Build procedure call sql - let procedureCallSql = `CALL "${schema}"."${procedure}" (` + procedureParameters.map(() => "?").toString() + `)`; - // Prepare call statement, set in params and register out params - procedureCallStatement = dConnection.prepareCall(procedureCallSql); - setProcedureParams(procedureCallStatement, procedureParameters); - // Execute the procedure call and get the resultsets + return "?"; + }).join(",")})`; + procedureCallStatement = dConnection.prepareCall(sql); + PROCEDURE_UTILS.setProcedureParameters(procedureCallStatement, procedureParameters); let hasResults = procedureCallStatement.execute(); let resultSets = []; do { @@ -114,16 +98,35 @@ function XscConnection(dConnection) { hasResults = procedureCallStatement.getMoreResults(); } while (hasResults); procedureResult.$resultSets = resultSets; + // Get the out paramter values - getOUTParamsValues(procedureCallStatement, procedureParameters, procedureResult, resultSets); + for (let i = 0, rsIndex = 0; i < procedureParameters.length; i++) { + const procedureParameter = procedureParameters[i]; + if (PROCEDURE_UTILS.isProcedureOutParamType(procedureParameter.parameterType)) { + let value = PROCEDURE_UTILS.getProcedureOutParamValue(procedureCallStatement, procedureParameter, resultSets[rsIndex]); + procedureResult[procedureParameter.parameterName] = value; + if (procedureParameter.parameterDataType === PROCEDURE_UTILS.SQL_TABLE_TYPE) { + rsIndex++; + } + } + } return procedureResult; } catch (e) { throw new Error(e); } finally { - if (procedureCallStatement !== null) { + for (let i = 0; i < temporaryTables.length; i++) { + try { + PROCEDURE_UTILS.dropTemporaryTable(dConnection, temporaryTables[i]); + } catch (e) { + console.error(`Error occurred when droping temporary table ${e}`); + } + + } + + if (procedureCallStatement !== null && procedureCallStatement !== undefined) { procedureCallStatement.close(); } - if (dConnection !== null) { + if (dConnection !== null && dConnection !== undefined) { dConnection.close(); } } @@ -169,8 +172,7 @@ function XscResultSet(dResultSet) { let len = metadata.columns.length; for (var i = 0; i < len; i++) { let dataType = metadata.columns[i].typeName; - let value = getResultSetValueByDataTypeAndRowNumber(dResultSet, dataType, i + 1); - // response.println("type: " + dataType + " / toString: " + value.toString()); + let value = HDB_UTILS.getResultSetValueByDataTypeAndRowNumber(dResultSet, dataType, i + 1); let propertyName = metadata.columns[i].name; objToReturn[propertyName] = value; } @@ -201,15 +203,6 @@ function SQLException() { } // UTIL FUNCTIONS -function setProcedureParams(dPreparedCallableStatement, procedureParameters) { - for (var i = 0, paramIndex = 1; i < procedureParameters.length; i++, paramIndex++) { - const procedureParameter = procedureParameters[i]; - if (procedureParameter.parameterType === PROCEDURE_IN_PARAMETER || procedureParameter.parameterType === PROCEDURE_IN_OUT_PARAMETER) { - setParamByType(dPreparedCallableStatement, procedureParameter.parameterTypeName, procedureParameter.parameterValue, paramIndex); - } - } -} - function setStatementParams(dPreparedStatement, args) { var parameterMetaData = dPreparedStatement.native.getParameterMetaData(); var paramsCount = parameterMetaData.getParameterCount(); @@ -219,81 +212,27 @@ function setStatementParams(dPreparedStatement, args) { for (var i = 0, paramIndex = 1; i < paramsCount; i++, paramIndex++) { var paramType = parameterMetaData.getParameterTypeName(paramIndex); var paramValue = args[i]; - setParamByType(dPreparedStatement, paramType, paramValue, paramIndex); + HDB_UTILS.setParamByType(dPreparedStatement, paramType, paramValue, paramIndex); } } -function setParamByType(dPreparedStatement, paramType, paramValue, paramIndex) { - switch (paramType) { - case 'TINYINT': - dPreparedStatement.setByte(paramIndex, paramValue); - break; - case 'SMALLINT': - dPreparedStatement.setShort(paramIndex, paramValue); - break; - case 'INTEGER': - dPreparedStatement.setInt(paramIndex, paramValue); - break; - case 'BIGINT': - dPreparedStatement.setLong(paramIndex, paramValue); - break; - case 'SMALLDECIMAL': - case 'DECIMAL': - dPreparedStatement.setBigDecimal(paramIndex, paramValue); - break; - case 'REAL': - case 'FLOAT': - dPreparedStatement.setFloat(paramIndex, paramValue); - break; - case 'DOUBLE': - dPreparedStatement.setDouble(paramIndex, paramValue); - break; - case 'VARCHAR': - case 'ALPHANUM': - dPreparedStatement.setString(paramIndex, paramValue); - break; - case 'NVARCHAR': - case 'NCHAR': - case 'SHORTTEXT': - dPreparedStatement.native.setNString(paramIndex, paramValue); - break; - case 'VARBINARY': - dPreparedStatement.setBytes(paramIndex, paramValue); - break; - case 'BOOLEAN': - dPreparedStatement.setBoolean(paramIndex, paramValue); - break; - case 'DATE': - dPreparedStatement.setDate(paramIndex, paramValue); - break; - case 'TIME': - dPreparedStatement.setTime(paramIndex, paramValue); - break; - case 'TIMESTAMP': - dPreparedStatement.setTimestamp(paramIndex, paramValue); - break; - case 'SECONDDATE': - // TODO - break; - case 'BLOB': - dPreparedStatement.setBlob(paramIndex, paramValue); - break; - case 'TEXT': - case 'CLOB': - dPreparedStatement.setClob(paramIndex, paramValue); - break; - case 'ARRAY': - // TODO - break; - case 'NCLOB': - dPreparedStatement.setNClob(paramIndex, paramValue); - break; - case 'ST_GEOMETRY': - // TODO - break; - case 'ST_POINT': - // TODO - break; +function setProcedureParameterValues(connection, procedureParameters, procedureCallArgs) { + if (procedureCallArgs.length === 1 && typeof procedureCallArgs[0] === 'object' && !Array.isArray(procedureCallArgs[0]) && procedureCallArgs[0] !== null) { + procedureCallArgs = procedureCallArgs[0]; + for (let procedureCallArg in procedureCallArgs) { + procedureParameters.forEach(procedureParameter => { + if (procedureParameter.parameterName === procedureCallArg.toUpperCase()) { + procedureParameter.parameterValue = procedureCallArgs[procedureCallArg]; + } + }) + } + } else { + for (let i = 0, argIndex = 0; i < procedureParameters.length; i++) { + if (PROCEDURE_UTILS.isProcedureInParamType(procedureParameters[i].parameterType)) { + procedureParameters[i].parameterValue = procedureCallArgs[argIndex]; + argIndex++; + } + } } } @@ -332,160 +271,4 @@ function getColumnMetadataArray(dResultSetMetaData) { }); } return columnMetadataArray; -} - -function getResultSetValueByDataTypeAndRowNumber(dResultSet, dataType, colNumber) { - switch (dataType) { - case 'TINYINT': - return dResultSet.getByte(colNumber); - case 'SMALLINT': - return dResultSet.getShort(colNumber); - case 'INTEGER': - return dResultSet.getInt(colNumber); - case 'BIGINT': - return dResultSet.getLong(colNumber); - case 'SMALLDECIMAL': - case 'DECIMAL': - //TODO: fix when getBigDecimal is fixed - return dResultSet.getBigDecimal(colNumber); - case 'REAL': - case 'FLOAT': - return dResultSet.getFloat(colNumber); - case 'DOUBLE': - return dResultSet.getDouble(colNumber); - case 'VARCHAR': - case 'ALPHANUM': - return dResultSet.getString(colNumber); - case 'NVARCHAR': - case 'SHORTTEXT': - return dResultSet.native.getNString(colNumber); - case 'VARBINARY': - return dResultSet.getBytes(colNumber); - case 'BOOLEAN': - return dResultSet.getBoolean(colNumber); - case 'DATE': - return dResultSet.getDate(colNumber); - case 'TIME': - return dResultSet.getTime(colNumber); - case 'TIMESTAMP': - return dResultSet.getTimestamp(colNumber); - case 'SECONDDATE': - // TODO - break; - case 'BLOB': - return dResultSet.getBlob(colNumber); - case 'TEXT': - case 'CLOB': - //TODO needs work - return dResultSet.getClob(colNumber); - case 'ARRAY': - //TODO to do - break; - case 'NCLOB': - return dResultSet.getNClob(colNumber); - case 'ST_GEOMETRY': - // TODOto do - break; - case 'ST_POINT': - //TODO to do - break; - } -} - -function getOUTParamsValues(procedureCallStatement, procedureParameters, procedureResult, resultSets) { - for (let i = 0, rsIndex = 0; i < procedureParameters.length; i++) { - const procedureParameter = procedureParameters[i]; - if (procedureParameter.parameterType === PROCEDURE_IN_OUT_PARAMETER || procedureParameter.parameterType === PROCEDURE_OUT_PARAMETER) { - switch (procedureParameter.parameterDataType) { - case java.sql.Types.TINYINT: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getByte(procedureParameter.parameterName); - break; - case java.sql.Types.VARCHAR: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getString(procedureParameter.parameterName); - break; - case java.sql.Types.NVARCHAR: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getNString(procedureParameter.parameterName); - break; - case java.sql.Types.ARRAY: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getArray(procedureParameter.parameterName); - break; - case java.sql.Types.BIGINT: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getInt(procedureParameter.parameterName); - break; - case java.sql.Types.BOOLEAN: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getBoolean(procedureParameter.parameterName); - break; - case java.sql.Types.INTEGER: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getInt(procedureParameter.parameterName); - break; - case java.sql.Types.TIME: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getTime(procedureParameter.parameterName); - break; - case java.sql.Types.TIMESTAMP: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getTimestamp(procedureParameter.parameterName); - break; - case java.sql.Types.BLOB: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getBlob(procedureParameter.parameterName); - break; - case java.sql.Types.DATE: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getDate(procedureParameter.parameterName); - break; - case java.sql.Types.DOUBLE: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getDouble(procedureParameter.parameterName); - break; - case java.sql.Types.FLOAT: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getFloat(procedureParameter.parameterName); - break; - case java.sql.Types.BIGINT: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getInt(procedureParameter.parameterName); - break; - case java.sql.Types.DECIMAL: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getBigDecimal(procedureParameter.parameterName); - break; - case java.sql.Types.SQLXML: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getSQLXML(procedureParameter.parameterName); - break; - case java.sql.Types.URL: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getUrl(procedureParameter.parameterName); - break; - case java.sql.Types.NUMERIC: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getInt(procedureParameter.parameterName); - break; - case java.sql.Types.NCLOB: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getNClob(procedureParameter.parameterName); - break; - case java.sql.Types.CLOB: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getClob(procedureParameter.parameterName); - break; - case java.sql.Types.NCHAR: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getNString(procedureParameter.parameterName); - break; - case java.sql.Types.REF: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getRef(procedureParameter.parameterName); - break; - case java.sql.Types.JAVA_OBJECT: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getObject(procedureParameter.parameterName); - break; - case java.sql.Types.LONG: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getLong(procedureParameter.parameterName); - break; - case java.sql.Types.ROWID: - procedureResult[procedureParameter.parameterName] = procedureCallStatement.getRowId(procedureParameter.parameterName); - break; - case SQL_TABLE_TYPE: - const tableResultSet = resultSets[rsIndex]; - let tableObj = {}; - let length = 0; - for (let i = 0; i < tableResultSet.length; i++) { - let rowObj = tableResultSet[i]; - tableObj[i] = rowObj; - length++; - } - tableObj.length = length; - procedureResult[procedureParameter.parameterName] = tableObj; - rsIndex++; - break; - } - } - } -} +} \ No newline at end of file diff --git a/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdbUtils.js b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdbUtils.js new file mode 100644 index 000000000..85c10618e --- /dev/null +++ b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/hdbUtils.js @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022 SAP SE or an SAP affiliate company and XSK contributors + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, v2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and XSK contributors + * SPDX-License-Identifier: Apache-2.0 + */ +exports.getResultSetValueByDataTypeAndRowNumber = function (resultSet, dataType, colNumber) { + switch (dataType) { + case "TINYINT": + return resultSet.getByte(colNumber); + case "SMALLINT": + return resultSet.getShort(colNumber); + case "INTEGER": + return resultSet.getInt(colNumber); + case "BIGINT": + return resultSet.getLong(colNumber); + case "SMALLDECIMAL": + case "DECIMAL": + return resultSet.getBigDecimal(colNumber); + case "REAL": + case "FLOAT": + return resultSet.getFloat(colNumber); + case "DOUBLE": + return resultSet.getDouble(colNumber); + case "VARCHAR": + case "ALPHANUM": + return resultSet.getString(colNumber); + case "NVARCHAR": + case "SHORTTEXT": + return resultSet.native.getNString(colNumber); + case "VARBINARY": + return resultSet.getBytes(colNumber); + case "BOOLEAN": + return resultSet.getBoolean(colNumber); + case "DATE": + return resultSet.getDate(colNumber); + case "TIME": + return resultSet.getTime(colNumber); + case "TIMESTAMP": + return resultSet.getTimestamp(colNumber); + case "SECONDDATE": + throw new Error(`The '${dataType}' data type in the resultSet is not supported`); + case "BLOB": + return resultSet.getBlob(colNumber); + case "TEXT": + case "CLOB": + return resultSet.getClob(colNumber); + case "ARRAY": + throw new Error(`The '${dataType}' data type in the resultSet is not supported`); + case "NCLOB": + return resultSet.getNClob(colNumber); + case "ST_GEOMETRY": + throw new Error(`The '${dataType}' data type in the resultSet is not supported`); + case "ST_POINT": + throw new Error(`The '${dataType}' data type in the resultSet is not supported`); + default: + throw new Error(`The '${dataType}' data type in the resultSet is not supported`); + } +}; + +exports.setParamByType = function (preparedStatement, paramType, paramValue, paramIndex) { + switch (paramType) { + case "TINYINT": + preparedStatement.setByte(paramIndex, paramValue); + break; + case "SMALLINT": + preparedStatement.setShort(paramIndex, paramValue); + break; + case "INTEGER": + preparedStatement.setInt(paramIndex, paramValue); + break; + case "BIGINT": + preparedStatement.setLong(paramIndex, paramValue); + break; + case "SMALLDECIMAL": + case "DECIMAL": + preparedStatement.setBigDecimal(paramIndex, paramValue); + break; + case "REAL": + case "FLOAT": + preparedStatement.setFloat(paramIndex, paramValue); + break; + case "DOUBLE": + preparedStatement.setDouble(paramIndex, paramValue); + break; + case "VARCHAR": + case "ALPHANUM": + preparedStatement.setString(paramIndex, paramValue); + break; + case "NVARCHAR": + case "NCHAR": + case "SHORTTEXT": + preparedStatement.native.setNString(paramIndex, paramValue); + break; + case "VARBINARY": + preparedStatement.setBytes(paramIndex, paramValue); + break; + case "BOOLEAN": + preparedStatement.setBoolean(paramIndex, paramValue); + break; + case "DATE": + preparedStatement.setDate(paramIndex, paramValue); + break; + case "TIME": + preparedStatement.setTime(paramIndex, paramValue); + break; + case "TIMESTAMP": + preparedStatement.setTimestamp(paramIndex, paramValue); + break; + case "SECONDDATE": + throw new Error(`The '${paramType}' data type in the preparedStatement is not supported`); + case "BLOB": + preparedStatement.setBlob(paramIndex, paramValue); + break; + case "TEXT": + case "CLOB": + preparedStatement.setClob(paramIndex, paramValue); + break; + case "ARRAY": + throw new Error(`The '${paramType}' data type in the preparedStatement is not supported`); + case "NCLOB": + preparedStatement.setNClob(paramIndex, paramValue); + break; + case "ST_GEOMETRY": + throw new Error(`The '${paramType}' data type in the preparedStatement is not supported`); + case "ST_POINT": + throw new Error(`The '${paramType}' data type in the preparedStatement is not supported`); + default: + throw new Error(`The '${paramType}' data type in the preparedStatement is not supported`); + } +}; \ No newline at end of file diff --git a/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/procedureUtils.js b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/procedureUtils.js new file mode 100644 index 000000000..be4dae05a --- /dev/null +++ b/modules/api/api-xsjs/src/main/resources/META-INF/dirigible/xsk/hdb/procedureUtils.js @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2022 SAP SE or an SAP affiliate company and XSK contributors + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, v2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and XSK contributors + * SPDX-License-Identifier: Apache-2.0 + */ +var HDB_UTILS = require('xsk/hdb/hdbUtils'); + +exports.SQL_TABLE_TYPE = 0; +exports.PROCEDURE_IN_PARAMETER = 1; +exports.PROCEDURE_IN_OUT_PARAMETER = 2; +exports.PROCEDURE_OUT_PARAMETER = 4; + +exports.isProcedureInParamType = function (parameterType) { + return parameterType === exports.PROCEDURE_IN_PARAMETER || parameterType === exports.PROCEDURE_IN_OUT_PARAMETER; +}; + +exports.isProcedureOutParamType = function (parameterType) { + return parameterType === exports.PROCEDURE_IN_OUT_PARAMETER || parameterType === exports.PROCEDURE_OUT_PARAMETER; +}; + +exports.isProcedureParamDataTypeTable = function (parameterDataType) { + return parameterDataType === exports.SQL_TABLE_TYPE; +}; + +exports.getProcedureParameters = function (connection, procedureName) { + let parameters = []; + let resultSet = connection.native.getMetaData().getProcedureColumns(null, null, procedureName, "%"); + + while (resultSet.next()) { + parameters.push({ + parameterName: resultSet.getString("COLUMN_NAME"), + parameterType: resultSet.getShort("COLUMN_TYPE"), + parameterTypeName: resultSet.getString("TYPE_NAME"), + parameterTypeSchema: resultSet.getString("PROCEDURE_SCHEM"), + parameterDataType: resultSet.getInt("DATA_TYPE"), + parameterValue: null, + isTableType: exports.isProcedureParamDataTypeTable(resultSet.getInt("DATA_TYPE")), + temporaryTableName: "", + temporaryTableType: "" + }) + } + parameters.filter(e => e.isTableType).forEach(e => { + e.temporaryTableName = `#TEMP_TABLE_${e.parameterTypeName}`; + e.temporaryTableType = `"${e.parameterTypeSchema}"."${e.parameterTypeName}"`; + }); + return parameters; +}; + +exports.setProcedureParameters = function (callableStatement, parameters) { + for (var i = 0, paramIndex = 1; i < parameters.length; i++) { + if (exports.isProcedureInParamType(parameters[i].parameterType) && !exports.isProcedureParamDataTypeTable(parameters[i].parameterDataType)) { + HDB_UTILS.setParamByType(callableStatement, parameters[i].parameterTypeName, parameters[i].parameterValue, paramIndex); + paramIndex++; + } + } +}; + +exports.getProcedureOutParamValue = function (procedureCallStatement, procedureParameter, resultSet) { + switch (procedureParameter.parameterDataType) { + case java.sql.Types.TINYINT: + return procedureCallStatement.getByte(procedureParameter.parameterName); + case java.sql.Types.VARCHAR: + return procedureCallStatement.getString(procedureParameter.parameterName); + case java.sql.Types.NVARCHAR: + return procedureCallStatement.getNString(procedureParameter.parameterName); + case java.sql.Types.ARRAY: + return procedureCallStatement.getArray(procedureParameter.parameterName); + case java.sql.Types.BIGINT: + return procedureCallStatement.getInt(procedureParameter.parameterName); + case java.sql.Types.BOOLEAN: + return procedureCallStatement.getBoolean(procedureParameter.parameterName); + case java.sql.Types.INTEGER: + return procedureCallStatement.getInt(procedureParameter.parameterName); + case java.sql.Types.TIME: + return procedureCallStatement.getTime(procedureParameter.parameterName); + case java.sql.Types.TIMESTAMP: + return procedureCallStatement.getTimestamp(procedureParameter.parameterName); + case java.sql.Types.BLOB: + return procedureCallStatement.getBlob(procedureParameter.parameterName); + case java.sql.Types.DATE: + return procedureCallStatement.getDate(procedureParameter.parameterName); + case java.sql.Types.DOUBLE: + return procedureCallStatement.getDouble(procedureParameter.parameterName); + case java.sql.Types.FLOAT: + return procedureCallStatement.getFloat(procedureParameter.parameterName); + case java.sql.Types.BIGINT: + return procedureCallStatement.getInt(procedureParameter.parameterName); + case java.sql.Types.DECIMAL: + return procedureCallStatement.getBigDecimal(procedureParameter.parameterName); + case java.sql.Types.SQLXML: + return procedureCallStatement.getSQLXML(procedureParameter.parameterName); + case java.sql.Types.URL: + return procedureCallStatement.getUrl(procedureParameter.parameterName); + case java.sql.Types.NUMERIC: + return procedureCallStatement.getInt(procedureParameter.parameterName); + case java.sql.Types.NCLOB: + return procedureCallStatement.getNClob(procedureParameter.parameterName); + case java.sql.Types.CLOB: + return procedureCallStatement.getClob(procedureParameter.parameterName); + case java.sql.Types.NCHAR: + return procedureCallStatement.getNString(procedureParameter.parameterName); + case java.sql.Types.REF: + return procedureCallStatement.getRef(procedureParameter.parameterName); + case java.sql.Types.JAVA_OBJECT: + return procedureCallStatement.getObject(procedureParameter.parameterName); + case java.sql.Types.LONG: + return procedureCallStatement.getLong(procedureParameter.parameterName); + case java.sql.Types.ROWID: + return procedureCallStatement.getRowId(procedureParameter.parameterName); + case exports.SQL_TABLE_TYPE: + let tableObj = { + length: 0 + }; + for (let i = 0; i < resultSet.length; i++) { + tableObj[i] = resultSet[i]; + tableObj.length++; + } + return tableObj; + default: + throw new Error(`Unsupported procedure result OUT parameter data type '${procedureParameter.parameterDataType}'`); + } +}; + +// ----------------------------------------------------------------------------------------------------------------- +// Related to #1464 [API] Procedure - Input parameter must be a table (https://github.com/SAP/xsk/issues/1464) +// ----------------------------------------------------------------------------------------------------------------- +exports.dropTemporaryTable = function (connection, tableName) { + let sql = `DROP TABLE ${tableName}`; + let ps = null; + try { + ps = connection.prepareStatement(sql); + isCreated = ps.executeUpdate(); + } finally { + if (ps !== null && ps !== undefined) { + ps.close(); + } + } +}; + +exports.createTemporaryTable = function (connection, tableName, tableType) { + let sql = `CREATE LOCAL TEMPORARY TABLE ${tableName} like ${tableType}`; + let ps = null; + try { + ps = connection.prepareStatement(sql); + isCreated = ps.executeUpdate(); + } finally { + if (ps !== null && ps !== undefined) { + ps.close(); + } + } +}; + +exports.insertTemporaryTableData = function (connection, tableName, row) { + let sql = `INSERT INTO ${tableName} (${Object.keys(row).join(",")}) VALUES (${Object.keys(row).map(() => "?").join(",")})`; + let ps = null; + try { + ps = connection.prepareStatement(sql); + // TODO: Insert data types based on the table metadata! + let objKeys = Object.keys(row); + for (let i = 0; i < objKeys.length; i++) { + let value = row[objKeys[i]] + if (Number.isInteger(value)) { + ps.setInt(i + 1, value); + } else { + ps.setString(i + 1, value); + } + } + isCreated = ps.executeUpdate(); + } finally { + if (ps !== null && ps !== undefined) { + ps.close(); + } + } + return sql; +}; \ No newline at end of file