diff --git a/ibm_db.c b/ibm_db.c index ad4a3896..b4c5298f 100644 --- a/ibm_db.c +++ b/ibm_db.c @@ -171,6 +171,8 @@ typedef struct _param_cache_node SQLINTEGER *ivalueArray; /* Temp storage array of values */ double *fvalueArray; /* Temp storage array of values */ SQLINTEGER *bind_indicator_array; /* Temp storage array of values */ + SQLSMALLINT cardinality; + SQLSMALLINT actual_cardinality; struct _param_cache_node *next; /* Pointer to next node */ } param_node; @@ -288,6 +290,7 @@ typedef struct _stmt_handle_struct int num_columns; ibm_db_result_set_info *column_info; ibm_db_row_type *row_data; + int is_stored_procedure; } stmt_handle; static void _python_ibm_db_free_stmt_struct(stmt_handle *handle); @@ -401,6 +404,171 @@ char *strtoupper(char *data, int max) } return data; } +/* + * This function sets the cardinality fields in the parameter descriptors so DB2 + * CLI knows how many elements are in each array and where to find the actual + * cardinality. + * + * Cardinality and buffer allocation are applied using SQLSetDescField() with: + * - SQL_DESC_CARDINALITY : maximum cardinality of the array + * - SQL_DESC_CARDINALITY_PTR : pointer to a variable containing the actual + * cardinality at runtime + * + * Descriptor handles are chosen based on the stored procedure argument type: + * - INPUT arguments : Implementation Parameter Descriptor (hIPD) + * - OUTPUT arguments : Application Parameter Descriptor (hAPD) + * - INOUT arguments : both hIPD and hAPD + * + * For each array parameter, SQLSetDescField() is called on the appropriate + * descriptor(s) to set both SQL_DESC_CARDINALITY and SQL_DESC_CARDINALITY_PTR. + * This ensures DB2 CLI correctly interprets array parameter cardinality when + * executing the stored procedure. + */ + +#ifndef __MVS__ +static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, + param_node *curr, + SQLSMALLINT cardinality) +{ + + SQLHDESC hIPD = (SQLHDESC)0; + SQLHDESC hAPD = (SQLHDESC)0; + SQLRETURN rc; + curr->cardinality = cardinality; + curr->actual_cardinality = cardinality; + + LogMsg(INFO, "entry _python_ibm_db_set_array_param_cardinality()"); + + snprintf(messageStr, sizeof(messageStr), + "Setting array param descriptor fields: param_num=%d, param_type=%d, cardinality=%d", + curr->param_num, curr->param_type, cardinality); + LogMsg(DEBUG, messageStr); + + rc = SQLGetStmtAttr(stmt_res->hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, 0, NULL); + if (rc != SQL_SUCCESS || hIPD == NULL) { + LogMsg(ERROR, "Failed to get implicit parameter descriptor handle (hIPD)"); + } else { + snprintf(messageStr, sizeof(messageStr), + "Set hIPD descriptor handle=%p", (void*)hIPD); + LogMsg(DEBUG, messageStr); + } + + rc = SQLGetStmtAttr(stmt_res->hstmt, SQL_ATTR_APP_PARAM_DESC, &hAPD, 0, NULL); + if (rc != SQL_SUCCESS || hAPD == NULL) { + LogMsg(ERROR, "Failed to get application parameter descriptor handle (hAPD)"); + } else { + snprintf(messageStr, sizeof(messageStr), + "Set hAPD descriptor handle=%p", (void*)hAPD); + LogMsg(DEBUG, messageStr); + } + + if (curr->param_type == SQL_PARAM_INPUT && hIPD != NULL) { + rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY, + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hIPD(INPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hIPD(INPUT)"); + } + + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + } + } + + else if (curr->param_type == SQL_PARAM_OUTPUT && hAPD != NULL) { + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY, + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hAPD(OUTPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hAPD(OUTPUT)"); + } + + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hAPD(OUTPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hAPD(OUTPUT)"); + } + } + + else if (curr->param_type == SQL_PARAM_INPUT_OUTPUT) { + if (hIPD != NULL) { + rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY, + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hIPD(INOUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hIPD(INOUT)"); + } + + rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hIPD(INOUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hIPD(INOUT)"); + } + } + + if (hAPD != NULL) { + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY, + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hAPD(INOUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hAPD(INOUT)"); + } + + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hAPD(INOUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hAPD(INOUT)"); + } + } + } + + LogMsg(INFO, "exit _python_ibm_db_set_array_param_cardinality()"); +} +#endif + +int sp_starts_with_call(SQLWCHAR *sql) +{ + LogMsg(INFO, "entry sp_starts_with_call()"); + while (*sql == L' ' || *sql == L'\t' || *sql == L'\n') { + sql++; + } + // Compare with "CALL " (case-insensitive) + if (towupper(sql[0]) == L'C' && + towupper(sql[1]) == L'A' && + towupper(sql[2]) == L'L' && + towupper(sql[3]) == L'L' && + (sql[4] == L' ' || sql[4] == L'\0')) { + LogMsg(DEBUG, "sp_starts_with_call: matched 'CALL'"); + return 1; + } + + // Compare with "{CALL" (case-insensitive) + if (sql[0] == L'{' && + towupper(sql[1]) == L'C' && + towupper(sql[2]) == L'A' && + towupper(sql[3]) == L'L' && + towupper(sql[4]) == L'L') { + LogMsg(DEBUG, "sp_starts_with_call: matched '{CALL'"); + return 1; + } + LogMsg(DEBUG, "sp_starts_with_call: no match"); + return 0; + LogMsg(INFO, "exit sp_starts_with_call()"); +} PyObject* format_timestamp_pystr(const TIMESTAMP_STRUCT_EXT_TZ* ts) { char formatted[160]; @@ -7166,6 +7334,14 @@ static int _python_ibm_db_do_prepare(SQLHANDLE hdbc, SQLWCHAR *stmt, int stmt_si #endif Py_END_ALLOW_THREADS; + if (sp_starts_with_call(stmt)) { + stmt_res->is_stored_procedure = 1; + LogMsg(DEBUG, "Statement starts with CALL: marked as stored procedure"); + } else { + stmt_res->is_stored_procedure = 0; + LogMsg(DEBUG, "Statement does not start with CALL: not a stored procedure"); + } + if (rc == SQL_ERROR) { _python_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, @@ -7886,6 +8062,19 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter with parameters: hstmt=%p, param_num=%d, param_type=%d, c_type=%d, data_type=%d, precision=%lu, scale=%d, svalue=%s, buffer_length=%lu, bind_indicator_array=%p and returned rc:%d", (void *)stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_CHAR, curr->data_type, (unsigned long)MAX_PRECISION, curr->scale, curr->svalue, (unsigned long)MAX_PRECISION, (void *)&curr->bind_indicator_array[0], rc); LogMsg(DEBUG, messageStr); +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr), + "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif } else { @@ -7957,6 +8146,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter with hstmt=%p, param_num=%d, param_type=%d, c_type=%d, data_type=%d, param_size=%lu, scale=%d, ivalueArray=%p, buffer_length=%lu, bind_indicator_array=%p, rc=%d", (void *)stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, (unsigned long)curr->param_size, curr->scale, (void *)curr->ivalueArray, (unsigned long)0, (void *)&curr->bind_indicator_array[0], rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr), + "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -8021,6 +8225,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter with parameters: hstmt=%p, param_num=%d, param_type=%d, c_type=%d, data_type=%d, param_size=%lu, scale=%d, ivalueArray=%p, bind_indicator_array=%p, and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, curr->param_size, curr->scale, curr->ivalueArray, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -8088,6 +8307,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter with parameters: hstmt=%p, param_num=%d, param_type=%d, c_type=%d, data_type=%d, param_size=%lu, scale=%d, ivalueArray=%p, bind_indicator_array=%p, and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, curr->param_size, curr->scale, curr->ivalueArray, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -8152,6 +8386,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter with parameters: hstmt=%p, param_num=%d, param_type=%d, c_type=%d, data_type=%d, param_size=%lu, scale=%d, fvalueArray=%p, bind_indicator_array=%p, and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_DOUBLE, curr->data_type, curr->param_size, curr->scale, curr->fvalueArray, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -8188,6 +8437,9 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO Py_ssize_t n = PyList_Size(bind_data); snprintf(messageStr, sizeof(messageStr), "PYTHON_LIST detected with size: %zd", n); LogMsg(DEBUG, messageStr); + //For Null terminator + if (curr->data_type == SQL_TYPE_TIMESTAMP) + curr->param_size = curr->param_size + sizeof(SQLWCHAR); curr->uvalue = (SQLWCHAR *)ALLOC_N(SQLWCHAR, curr->param_size * (n)); curr->bind_indicator_array = (SQLINTEGER *)ALLOC_N(SQLINTEGER, n); memset(curr->uvalue, 0, sizeof(SQLWCHAR) * curr->param_size * n); @@ -8389,7 +8641,24 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Default case for data_type: %d", curr->data_type); LogMsg(DEBUG, messageStr); valueType = SQL_C_WCHAR; - curr->bind_indicator_array[i] = param_length; + if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT){ + curr->bind_indicator_array[i] = param_length; + paramValuePtr = (SQLPOINTER)curr->uvalue; + snprintf(messageStr, sizeof(messageStr), "SQL_PARAM_OUTPUT or SQL_PARAM_INPUT_OUTPUT:" + " bind_indicator_array[%d]=%d, paramValuePtr=%p", + i, curr->bind_indicator_array[i], (void *)paramValuePtr); + LogMsg(DEBUG, messageStr); + } else { + curr->bind_indicator_array[i] = curr->ivalue; +#ifndef PASE + paramValuePtr = (SQLPOINTER)(curr->uvalue); +#else + paramValuePtr = (SQLPOINTER) & (curr->uvalue); +#endif + snprintf(messageStr, sizeof(messageStr), "Non SQL_PARAM_OUTPUT/SQL_PARAM_INPUT_OUTPUT: " + "bind_indicator_array[%d]=%d, paramValuePtr=%p", i, curr->bind_indicator_array[i], (void *)paramValuePtr); + LogMsg(DEBUG, messageStr); + } paramValuePtr = (SQLPOINTER)(curr->uvalue); snprintf(messageStr, sizeof(messageStr), "Set bind_indicator_array[%d]=%d, valueType=%d, paramValuePtr=%p", i, curr->bind_indicator_array[i], valueType, (void *)paramValuePtr); @@ -8407,13 +8676,25 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO stmt_res->hstmt, curr->param_num, curr->param_type, valueType, curr->data_type, curr->param_size, curr->scale, paramValuePtr, param_size, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + + if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO) { _python_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } - snprintf(messageStr, sizeof(messageStr), "Updated curr->data_type to %d", curr->data_type); - LogMsg(DEBUG, messageStr); - curr->data_type = valueType; } else /* To bind scalar values */ { @@ -8632,9 +8913,6 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO { _python_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } - curr->data_type = valueType; - snprintf(messageStr, sizeof(messageStr), "Updated curr->data_type to %d", curr->data_type); - LogMsg(DEBUG, messageStr); } } break; @@ -8649,6 +8927,9 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO Py_ssize_t n = PyList_Size(bind_data); snprintf(messageStr, sizeof(messageStr), "Binding array of values: n=%zd", n); LogMsg(DEBUG, messageStr); + //For Null terminator + if (curr->data_type == SQL_TYPE_TIMESTAMP) + curr->param_size = curr->param_size + 1; curr->svalue = (char *)ALLOC_N(char, curr->param_size *(n)); curr->bind_indicator_array = (SQLINTEGER *)ALLOC_N(SQLINTEGER, n); memset(curr->svalue, 0, curr->param_size * n); @@ -8856,6 +9137,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO (void *)stmt_res->hstmt, curr->param_num, curr->param_type, valueType, curr->data_type, curr->param_size, curr->scale, (void *)paramValuePtr, curr->param_size, curr->bind_indicator_array[0], rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO) { _python_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, @@ -9144,6 +9440,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter for PYTHON_LIST with parameters: hstmt=%p, param_num=%d, param_type=%d, valueType=%d, data_type=%d, param_size=%d, scale=%d, paramValuePtr=%p, max_precn=%d, bind_indicator_array=%p and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, valueType, curr->data_type, curr->param_size, curr->scale, paramValuePtr, max_precn, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO) { _python_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); @@ -9235,6 +9546,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter for PYTHON_LIST with parameters: hstmt=%p, param_num=%d, param_type=%d, data_type=%d, param_size=%d, scale=%d, date_value=%p, ivalue=%d, bind_indicator_array=%p and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, curr->data_type, curr->param_size, curr->scale, curr->date_value, curr->ivalue, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -9296,6 +9622,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter for PYTHON_LIST with parameters: hstmt=%p, param_num=%d, param_type=%d, data_type=%d, param_size=%d, scale=%d, time_value=%p, ivalue=%d, bind_indicator_array=%p and returned rc=%d", stmt_res->hstmt, curr->param_num, curr->param_type, curr->data_type, curr->param_size, curr->scale, curr->time_value, curr->ivalue, curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -9365,6 +9706,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO snprintf(messageStr, sizeof(messageStr), "Called SQLBindParameter for PYTHON_LIST with parameters: hstmt=%p, param_num=%d, param_type=%d, data_type=%d, param_size=%d, scale=%d, ts_value=%p, ivalue=%d, bind_indicator_array=%p and returned rc=%d", (void *)stmt_res->hstmt, curr->param_num, curr->param_type, curr->data_type, curr->param_size, curr->scale, (void *)curr->ts_value, curr->ivalue, (void *)curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -9482,6 +9838,20 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO (void *)stmt_res->hstmt, curr->param_num, curr->param_type, curr->data_type, curr->param_size, curr->scale, (void *)curr->tstz_value, curr->ivalue, (void *)curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -9587,6 +9957,21 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO (void *)stmt_res->hstmt, curr->param_num, curr->param_type, curr->data_type, curr->param_size, curr->scale, (void *)curr->ivalueArray, (void *)curr->bind_indicator_array, rc); LogMsg(DEBUG, messageStr); + +#ifndef __MVS__ + if (stmt_res->is_stored_procedure) { + snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," + " cardinality=%d, actual_cardinality=%d, n(array size)=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + LogMsg(DEBUG, messageStr); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + snprintf(messageStr, sizeof(messageStr), + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", + curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + LogMsg(DEBUG, messageStr); + } +#endif + } else { @@ -17521,6 +17906,809 @@ static PyObject *ibm_db_fetchall(PyObject *self, PyObject *args) return result_list; } +static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) +{ + LogMsg(INFO, "entry ibm_db_fetch_callproc"); + PyObject *py_stmt_res; + int len_terChar = 0; + SQLSMALLINT targetCType = SQL_C_CHAR; + + if (!PyArg_ParseTuple(args, "O", &py_stmt_res)) { + LogMsg(ERROR, "ibm_db_fetch_callproc: failed to parse arguments"); + return NULL; + } + + stmt_handle *stmt_res = (stmt_handle*)py_stmt_res; + int numOfParam = stmt_res->num_params; + + if (numOfParam == 0) { + Py_INCREF(py_stmt_res); + LogMsg(DEBUG, "ibm_db_fetch_callproc: no parameters to process"); + Py_INCREF(py_stmt_res); + return py_stmt_res; + } + + param_node *curr = stmt_res->head_cache_list; + curr->cardinality = curr->actual_cardinality; + snprintf(messageStr, sizeof(messageStr), "ibm_db_fetch_callproc: num_params=%d", numOfParam); + LogMsg(DEBUG, messageStr); + + PyObject *outTuple = PyTuple_New(numOfParam + 1); + if (!outTuple) { + LogMsg(ERROR, "ibm_db_fetch_callproc: failed to allocate output tuple"); + return NULL; + } + + Py_INCREF(py_stmt_res); + PyTuple_SET_ITEM(outTuple, 0, py_stmt_res); + + int idx = 1; + while (curr && idx <= numOfParam) { + snprintf(messageStr, sizeof(messageStr), "Processing parameter %d (param_type=%d, data_type=%d, cardinality=%d)", + curr->param_num, curr->param_type, curr->data_type, curr->cardinality); + LogMsg(DEBUG, messageStr); + PyObject *pyVal = Py_None; + Py_INCREF(pyVal); + + if (curr->bind_indicator != SQL_NULL_DATA && + curr->bind_indicator != SQL_NO_TOTAL) { + + if (curr->cardinality > 1 && curr->actual_cardinality > 0) { + int len = curr->actual_cardinality; + PyObject *pyList = PyList_New(len); + if (!pyList) { + Py_DECREF(outTuple); + LogMsg(ERROR, "ibm_db_fetch_callproc: failed to allocate PyList for array output"); + return NULL; + } + + for (int i = 0; i < len; i++) { + PyObject *elem = Py_None; + Py_INCREF(elem); + + if (curr->bind_indicator_array && + curr->bind_indicator_array[i] != SQL_NULL_DATA && + curr->bind_indicator_array[i] != SQL_NO_TOTAL) { + int max_precn = (curr->data_type == SQL_DECIMAL) ? MAX_PRECISION : curr->param_size; + + switch (curr->data_type) { + case SQL_CHAR: { + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_CHAR array element using uvalue"); + SQLLEN ind_len = curr->bind_indicator_array[i]; + SQLWCHAR *elem_ptr = (SQLWCHAR *)((char *)curr->uvalue + i * curr->param_size); + snprintf(messageStr, sizeof(messageStr), + "Fetch loop: index=%d, ind_len=%ld, elem_ptr=%p, param_size=%d", + i, (long)ind_len, (void*)elem_ptr, curr->param_size); + LogMsg(DEBUG, messageStr); + LogMsg(DEBUG, "Convert SQLWCHAR buffer to Python Unicode"); + elem = getSQLWCharAsPyUnicodeObject(elem_ptr, ind_len); + if (!elem) { + LogMsg(DEBUG, "Conversion failed, falling back to raw bytes"); + PyErr_Clear(); + elem = PyBytes_FromStringAndSize((const char *)elem_ptr, ind_len); + } else { + LogMsg(DEBUG, "Conversion succeeded, attempting to clean output"); + if (PyUnicode_Check(elem)) { + PyObject *parts = PyObject_CallMethod(elem, "split", "s", "\x00"); + if (parts && PyList_Check(parts) && PyList_Size(parts) > 0) { + PyObject *first = PyList_GetItem(parts, 0); // borrowed ref + Py_INCREF(first); + Py_DECREF(parts); + PyObject *trimmed = PyObject_CallMethod(first, "rstrip", "s", " "); + Py_DECREF(first); + if (trimmed) { + Py_DECREF(elem); + elem = trimmed; + LogMsg(DEBUG, "split+rstrip succeeded, updated elem"); + } else { + PyErr_Clear(); + LogMsg(DEBUG, "rstrip failed, keeping original elem"); + } + } else { + PyErr_Clear(); + LogMsg(DEBUG, "split failed, keeping original elem"); + } + } + } + snprintf(messageStr, sizeof(messageStr),"Fetch loop exit: index=%d, elem=%p", i, (void*)elem); + LogMsg(DEBUG, messageStr); + } + + if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_CHAR array element using svalue"); + char *elem_ptr = curr->svalue + i * curr->param_size; + size_t max_len = curr->param_size; +#ifndef __MVS__ + size_t actual_len = strnlen(elem_ptr, max_len); +#endif + elem = PyBytes_FromStringAndSize(elem_ptr, actual_len); + if (!elem) { + LogMsg(DEBUG, "Failed to create PyBytes object"); + PyErr_Clear(); + } else { + snprintf(messageStr, sizeof(messageStr), "SQL_CHAR element final value (bytes): %.100s", elem_ptr); + LogMsg(DEBUG, messageStr); + } + } + if (curr->ts_value) { + LogMsg(DEBUG, "Processing SQL_CHAR timestamp array element using ts_value"); + elem = PyDateTime_FromDateAndTime( + curr->ts_value[i].year, + curr->ts_value[i].month, + curr->ts_value[i].day, + curr->ts_value[i].hour % 24, + curr->ts_value[i].minute, + curr->ts_value[i].second, + curr->ts_value[i].fraction / 1000); + } + if (curr->tstz_value){ + LogMsg(DEBUG,"Processing SQL_CHAR timestamp with timezone array element using tstz_value"); + elem = format_timestamp_pystr(curr->tstz_value); + } + break; + } + + case SQL_VARCHAR: { + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_VARCHAR array element using uvalue"); + SQLWCHAR *elem_ptr = curr->uvalue + i * (curr->param_size / sizeof(SQLWCHAR)); + SQLLEN actual_len; + if (curr->bind_indicator_array && curr->bind_indicator_array[i] > 0) { + actual_len = curr->bind_indicator_array[i]; + } else { + size_t max_chars = curr->param_size / sizeof(SQLWCHAR); + size_t len = 0; + while (len < max_chars && elem_ptr[len] != 0) { + len++; + } + actual_len = len * sizeof(SQLWCHAR); + } + PyObject *tmp = PyUnicode_DecodeUTF16((const char *)elem_ptr, actual_len, "strict", NULL); + if (tmp) { + PyObject *stripped = PyObject_CallMethod(tmp, "rstrip", NULL); + if (stripped) { + elem = stripped; + Py_DECREF(tmp); + } else { + elem = tmp; + } + } else { + PyErr_Clear(); + elem = PyBytes_FromStringAndSize((const char *)elem_ptr, actual_len); + } + } else if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_VARCHAR array element using svalue"); + char *elem_ptr = curr->svalue + i * curr->param_size; + size_t max_len = curr->param_size; +#ifndef __MVS__ + size_t actual_len = strnlen(elem_ptr, max_len); +#endif + elem = PyBytes_FromStringAndSize(elem_ptr, actual_len); + } else if (curr->ts_value) { + LogMsg(DEBUG, "Processing SQLVARCHAR array element using ts_value"); + elem = PyDateTime_FromDateAndTime( + curr->ts_value[i].year, + curr->ts_value[i].month, + curr->ts_value[i].day, + curr->ts_value[i].hour % 24, + curr->ts_value[i].minute, + curr->ts_value[i].second, + curr->ts_value[i].fraction / 1000); + } else if (curr->tstz_value){ + LogMsg(DEBUG,"Processing SQLVARCHAR array element using tstz_value"); + elem = format_timestamp_pystr(curr->tstz_value); + } else { + Py_INCREF(Py_None); + elem = Py_None; + } + Py_XINCREF(elem); + break; + } + + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY:{ + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_BINARY/SQL_VARBINARY/SQL_LONGVARBINARY array element using uvalue"); + char *elem_ptr = curr->uvalue + i * curr->param_size; + SQLLEN actual_len = (curr->bind_indicator_array && curr->bind_indicator_array[i] > 0) + ? curr->bind_indicator_array[i] : curr->param_size; + elem = PyBytes_FromStringAndSize(elem_ptr, actual_len); + if (!elem) { + PyErr_Clear(); + Py_INCREF(Py_None); + elem = Py_None; + } else { + snprintf(messageStr, sizeof(messageStr), + "Converted SQL_BINARY/SQL_VARBINARY/SQL_LONGVARBINARY element of length %ld to Python bytes", (long)actual_len); + LogMsg(DEBUG, messageStr); + } + } else if(curr->svalue){ + LogMsg(DEBUG, "Processing SQL_BINARY/SQL_VARBINARY/SQL_LONGVARBINARY array element using svalue"); + char *elem_ptr = curr->svalue + i * curr->param_size; +#ifndef __MVS__ + size_t actual_len = strnlen(elem_ptr, curr->param_size); +#endif + elem = PyBytes_FromStringAndSize(elem_ptr, actual_len); + if (!elem) { + LogMsg(ERROR, "Failed to convert SQL_BINARY/SQL_VARBINARY/SQL_LONGVARBINARY element to Python bytes"); + PyErr_Clear(); + Py_INCREF(Py_None); + elem = Py_None; + } else { + snprintf(messageStr, sizeof(messageStr),"Converted VARBINARY element of length %zu to Python bytes", actual_len); + LogMsg(DEBUG, messageStr); + } + } else { + Py_INCREF(Py_None); + elem = Py_None; + } + Py_XINCREF(elem); + break; + } + + case SQL_CLOB: + case SQL_BLOB:{ + SQLLEN ind_len = (curr->bind_indicator_array && curr->bind_indicator_array[i] > 0) ? curr->bind_indicator_array[i] : curr->param_size; + if (curr->uvalue) { + LogMsg(DEBUG, " Processing SQL_BLOB/SQL_CLOB array element using uvalue"); + SQLWCHAR *elem_ptr = (SQLWCHAR *)((char*)curr->uvalue + i * curr->param_size); + int num_wchars = ind_len / sizeof(SQLWCHAR); + elem = getSQLWCharAsPyUnicodeObject(elem_ptr, num_wchars); + if (!elem) { + LogMsg(DEBUG, "getSQLWCharAsPyUnicodeObject failed, trying UTF16 decode"); + PyErr_Clear(); + elem = PyUnicode_DecodeUTF16((const char *)elem_ptr, ind_len, "strict", NULL); + if (!elem) { + LogMsg(DEBUG, "UTF16 decode failed, falling back to raw bytes"); + PyErr_Clear(); + elem = PyBytes_FromStringAndSize((const char *)elem_ptr, ind_len); + } + } + } else if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_BLOB/SQL_CLOB array element using svalue"); + char *ptr = curr->svalue + i * curr->param_size; + elem = PyBytes_FromStringAndSize(ptr, ind_len); + snprintf(messageStr, sizeof(messageStr), "Array fetch exit: index=%d, elem=%p", i, (void*)elem); + LogMsg(DEBUG, messageStr); + } + else { + Py_INCREF(Py_None); + elem = Py_None; + } + Py_XINCREF(elem); + break; + } + + case SQL_BIGINT: { + LogMsg(DEBUG, "Processing SQL_BIGINT array element"); + char tempStr[64] = {0}; + int copy_len = (MAX_PRECISION < sizeof(tempStr) - 1) ? MAX_PRECISION : sizeof(tempStr) - 1; + memcpy(tempStr, curr->svalue + i * MAX_PRECISION, copy_len); + tempStr[copy_len] = '\0'; + snprintf(messageStr, sizeof(messageStr),"BIGINT buffer at index %d: '%s' (copy_len=%d)", i, tempStr, copy_len); + LogMsg(DEBUG, messageStr); + + int len = strlen(tempStr); + while (len > 0 && isspace(tempStr[len - 1])) { + tempStr[--len] = '\0'; + } + snprintf(messageStr, sizeof(messageStr),"Trimmed BIGINT string at index %d: '%s' (length=%d)", i, tempStr, len); + LogMsg(DEBUG, messageStr); + elem = PyLong_FromString(tempStr, NULL, 10); + if (elem == NULL) { + PyErr_Clear(); + elem = Py_None; + Py_INCREF(elem); + snprintf(messageStr, sizeof(messageStr),"BIGINT conversion failed at index %d for value '%s'. Returning Py_None.",i, tempStr); + LogMsg(DEBUG, messageStr); + } else { + snprintf(messageStr, sizeof(messageStr), + "Converted BIGINT string to PyLong at index %d: value=%s",i, tempStr); + LogMsg(DEBUG, messageStr); + } + break; + } + + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_DECFLOAT:{ + LogMsg(DEBUG, "Processing SQL_DECIMAL/SQL_NUMERIC/SQL_DECFLOAT array element"); + if (curr->svalue != NULL) { + char tempStr[128] = {0}; + memcpy(tempStr, curr->svalue + i * max_precn, max_precn); + tempStr[max_precn - 1] = '\0'; + + int len = strlen(tempStr); + while (len > 0 && tempStr[len - 1] == ' ') { + tempStr[--len] = '\0'; + } + snprintf(messageStr, sizeof(messageStr),"Trimmed DECIMAL/DECFLOAT string at index %d: '%s' (length=%d)", i, tempStr, len); + LogMsg(DEBUG, messageStr); + + char *dot = strchr(tempStr, '.'); + if (dot) { + char *end = tempStr + strlen(tempStr) - 1; + while (end > dot && *end == '0') { + *end-- = '\0'; + } + if (end == dot) *end = '\0'; + } + snprintf(messageStr, sizeof(messageStr), "Converting numeric string to Python Decimal: '%s'", tempStr); + LogMsg(DEBUG, messageStr); + PyObject *pyDecModule = PyImport_ImportModule("decimal"); + if (pyDecModule != NULL) { + PyObject *pyDecClass = PyObject_GetAttrString(pyDecModule, "Decimal"); + if (pyDecClass != NULL) { + PyObject *pyStr = PyUnicode_FromString(tempStr); + if (pyStr != NULL) { + Py_DECREF(elem); + elem = PyObject_CallFunctionObjArgs(pyDecClass, pyStr, NULL); + Py_DECREF(pyStr); + } + Py_DECREF(pyDecClass); + } + Py_DECREF(pyDecModule); + } + } + break; + } + + case SQL_INTEGER: + case SQL_SMALLINT: + LogMsg(DEBUG, "Processing SQL_INTEGER/SQL_SMALLINT array element"); + if (curr->ivalueArray) { + snprintf(messageStr, sizeof(messageStr), "ivalueArray[%d] = %ld", i, curr->ivalueArray[i]); + LogMsg(DEBUG, messageStr); + elem = PyLong_FromLong(curr->ivalueArray[i]); + } + break; + + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: + LogMsg(DEBUG, "Processing SQL_REAL/SQL_FLOAT/SQL_DOUBLE array element"); + if (curr->fvalueArray) { + snprintf(messageStr, sizeof(messageStr), "fvalueArray[%d] = %f", i, curr->fvalueArray[i]); + LogMsg(DEBUG, messageStr); + elem = PyFloat_FromDouble(curr->fvalueArray[i]); + } + break; + + case SQL_TYPE_DATE: + LogMsg(DEBUG, "Processing SQL_TYPE_DATE array element"); + if (curr->date_value) { + snprintf(messageStr, sizeof(messageStr),"date_value[%d] = %04d-%02d-%02d", i, + curr->date_value[i].year, curr->date_value[i].month, curr->date_value[i].day); + LogMsg(DEBUG, messageStr); + elem = PyDate_FromDate(curr->date_value[i].year, + curr->date_value[i].month,curr->date_value[i].day); + } + break; + + case SQL_TYPE_TIME: + LogMsg(DEBUG, "Processing SQL_TYPE_TIME array element"); + if (curr->time_value) { + snprintf(messageStr, sizeof(messageStr), + "time_value[%d] = %02d:%02d:%02d", i, + curr->time_value[i].hour % 24, + curr->time_value[i].minute, + curr->time_value[i].second); + LogMsg(DEBUG, messageStr); + elem = PyTime_FromTime(curr->time_value[i].hour % 24, + curr->time_value[i].minute, + curr->time_value[i].second, 0); + } + break; + + case SQL_TYPE_TIMESTAMP: + case SQL_TYPE_TIMESTAMP_WITH_TIMEZONE:{ + if(curr->svalue){ + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP array element using svalue"); + char *elem_ptr = curr->svalue + i * curr->param_size; + size_t max_len = curr->param_size; + SQLLEN ind_len = (curr->bind_indicator_array && curr->bind_indicator_array[0] > 0) ? curr->bind_indicator_array[0] : curr->param_size; + elem = PyBytes_FromStringAndSize(curr->svalue, ind_len); + } + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP/SQL_TIMESTAMP_WITH_TIMEZONE array element using uvalue"); + SQLWCHAR *elem_ptr = curr->uvalue; + SQLLEN actual_len = (curr->bind_indicator > 0) ? curr->bind_indicator : curr->param_size * sizeof(SQLWCHAR); + elem = getSQLWCharAsPyUnicodeObject(elem_ptr, actual_len); + if (!elem) { + LogMsg(ERROR, "getSQLWCharAsPyUnicodeObject returned NULL"); + PyErr_Clear(); + elem = PyUnicode_DecodeUTF16((const char *)elem_ptr, actual_len, "strict", NULL); + if (!elem) { + LogMsg(ERROR, "UTF16 decode failed, falling back to raw bytes"); + PyErr_Clear(); + elem = PyBytes_FromStringAndSize((const char *)elem_ptr, actual_len); + } + } else { + LogMsg(DEBUG, "Successfully created PyUnicode object from SQLWCHAR"); + PyObject *trimmed = PyObject_CallMethod(elem, "rstrip", "s", "\x00"); + if (trimmed) { + Py_DECREF(elem); + elem = trimmed; + } else { + LogMsg(ERROR, "PyObject_CallMethod rstrip failed"); + } + } + } + if (curr->ts_value) { + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP array element using ts_value"); + snprintf(messageStr, sizeof(messageStr), + "Timestamp values for index %d: year=%d, month=%d, day=%d, hour=%d, minute=%d, second=%d, fraction=%d",i, + curr->ts_value[i].year, + curr->ts_value[i].month, + curr->ts_value[i].day, + curr->ts_value[i].hour, + curr->ts_value[i].minute, + curr->ts_value[i].second, + curr->ts_value[i].fraction); + LogMsg(DEBUG, messageStr); + + elem = PyDateTime_FromDateAndTime( + curr->ts_value[i].year, + curr->ts_value[i].month, + curr->ts_value[i].day, + curr->ts_value[i].hour % 24, + curr->ts_value[i].minute, + curr->ts_value[i].second, + curr->ts_value[i].fraction / 1000); + if (!elem) { + LogMsg(ERROR, "PyDateTime_FromDateAndTime failed"); + } + } + if (curr->tstz_value) { + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP_WITH_TIMEZONE array element"); + elem = format_timestamp_pystr(&curr->tstz_value[i]); + snprintf(messageStr, sizeof(messageStr), + "Array timestamp_with_timezone[%d] = %04d-%02d-%02d %02d:%02d:%02d.%03d %+03d:%02d",i, + curr->tstz_value[i].year, curr->tstz_value[i].month, curr->tstz_value[i].day, + curr->tstz_value[i].hour % 24, curr->tstz_value[i].minute, curr->tstz_value[i].second, + (int)(curr->tstz_value[i].fraction / 1000), + curr->tstz_value[i].timezone_hour, curr->tstz_value[i].timezone_minute); + LogMsg(DEBUG, messageStr); + if (!elem) { + LogMsg(ERROR, "format_timestamp_pystr returned NULL"); + } else { + LogMsg(DEBUG, "Created Python string for timestamp with timezone"); + } + } + break; + } + + default: + snprintf(messageStr, sizeof(messageStr),"Handling array parameter: unknown data_type=%d", curr->data_type); + LogMsg(WARNING, messageStr); + Py_INCREF(Py_None); + pyVal = Py_None; + snprintf(messageStr, sizeof(messageStr),"Returning Py_None for unsupported data_type=%d", curr->data_type); + LogMsg(DEBUG, messageStr); + break; + } + } + PyList_SET_ITEM(pyList, i, elem); + } + PyTuple_SET_ITEM(outTuple, idx++, pyList); + } else { + + LogMsg(DEBUG, "Handling scalar parameter"); + switch (curr->data_type) { + case SQL_CHAR: { + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_CHAR scalar parameter using uvalue"); + SQLLEN ind_len = curr->bind_indicator; + snprintf(messageStr, sizeof(messageStr),"Scalar fetch: ind_len=%ld, curr->uvalue=%p, param_size=%d", + (long)ind_len, (void*)curr->uvalue, curr->param_size); + LogMsg(DEBUG, messageStr); + LogMsg(DEBUG, "Attempting to convert SQLWCHAR buffer to Python Unicode"); + pyVal = getSQLWCharAsPyUnicodeObject(curr->uvalue, ind_len); + if (!pyVal) { + LogMsg(DEBUG, "Conversion failed, falling back to raw bytes"); + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize((const char *)curr->uvalue, ind_len); + } else { + LogMsg(DEBUG, "Conversion succeeded, attempting to rstrip spaces"); + PyObject *trimmed = PyObject_CallMethod(pyVal, "rstrip", "s", " "); + if (trimmed) { + pyVal = trimmed; + LogMsg(DEBUG, "rstrip succeeded, updated pyVal"); + } else { + PyErr_Clear(); + LogMsg(DEBUG, "rstrip failed, keeping original pyVal"); + } + } + snprintf(messageStr, sizeof(messageStr),"Scalar fetch exit: elem=%p", (void*)pyVal); + LogMsg(DEBUG, messageStr); + } + + if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_CHAR scalar parameter using svalue"); + pyVal = PyUnicode_Decode(curr->svalue, curr->param_size, "utf-8", "strict"); + if (!pyVal) { + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize(curr->svalue, curr->param_size); + } + } + snprintf(messageStr, sizeof(messageStr), "SQL_CHAR scalar fetch exit: pyVal=%p", (void*)pyVal); + LogMsg(DEBUG, messageStr); + break; + } + + case SQL_VARCHAR: { + if (curr->uvalue) { + LogMsg(DEBUG, "Processing scalar SQL_VARCHAR using uvalue"); + SQLWCHAR *elem_ptr = (SQLWCHAR *)curr->uvalue; + SQLLEN actual_len; + size_t max_chars = curr->param_size / sizeof(SQLWCHAR); + size_t len = 0; + while (len < max_chars && elem_ptr[len] != 0) { + len++; + } + actual_len = (curr->bind_indicator && curr->bind_indicator > 0) ? curr->bind_indicator : len * sizeof(SQLWCHAR); + PyObject *tmp = PyUnicode_DecodeUTF16((const char *)elem_ptr, actual_len, "strict", NULL); + if (tmp) { + pyVal = tmp; + } else { + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize((const char *)elem_ptr, actual_len); + } + } else if (curr->svalue) { + LogMsg(DEBUG, "Processing scalar SQL_VARCHAR using svalue"); +#ifndef __MVS__ + size_t actual_len = strnlen(curr->svalue, curr->param_size); +#endif + pyVal = PyUnicode_Decode(curr->svalue, actual_len, "utf-8", "strict"); + if (!pyVal) { + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize(curr->svalue, actual_len); + } + } + snprintf(messageStr, sizeof(messageStr), "SQL_VARCHAR scalar fetch exit: pyVal=%p", (void*)pyVal); + LogMsg(DEBUG, messageStr); + break; + } + + case SQL_LONGVARBINARY: + case SQL_BINARY: + case SQL_VARBINARY:{ + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_LONGVARBINARY/SQL_BINARY/SQL_VARBINARY scalar parameter using uvalue"); + int num_wchars = curr->param_size / sizeof(SQLWCHAR); + pyVal = getSQLWCharAsPyUnicodeObject(curr->uvalue, num_wchars); + if (!pyVal) { + LogMsg(DEBUG, "getSQLWCharAsPyUnicodeObject failed, trying UTF16 decode"); + PyErr_Clear(); + pyVal = PyUnicode_DecodeUTF16((const char *)curr->uvalue, curr->param_size, "strict", NULL); + if (!pyVal) { + LogMsg(DEBUG, "UTF16 decode failed, falling back to raw bytes"); + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize((char *)curr->uvalue, curr->param_size); + } + } + } else if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_LONGVARBINARY/SQL_BINARY/SQL_VARBINARY scalar parameter using svalue"); + pyVal = PyBytes_FromStringAndSize(curr->svalue, curr->param_size); + } + snprintf(messageStr, sizeof(messageStr), "SQL_LONGVARBINARY/SQL_BINARY/SQL_VARBINARY scalar fetch exit: pyVal=%p", (void*)pyVal); + LogMsg(DEBUG, messageStr); + break; + } + + case SQL_BLOB: + case SQL_CLOB: { + SQLLEN ind_len = (curr->bind_indicator_array && curr->bind_indicator_array[0] > 0) + ? curr->bind_indicator_array[0] : curr->param_size; + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_BLOB/SQL_CLOB scalar parameter using uvalue"); + SQLWCHAR *elem_ptr = curr->uvalue; + int num_wchars = ind_len / sizeof(SQLWCHAR); + pyVal = getSQLWCharAsPyUnicodeObject(elem_ptr, num_wchars); + if (!pyVal) { + LogMsg(DEBUG, "getSQLWCharAsPyUnicodeObject failed, trying UTF16 decode"); + PyErr_Clear(); + pyVal = PyUnicode_DecodeUTF16((const char *)elem_ptr, ind_len, "strict", NULL); + if (!pyVal) { + LogMsg(DEBUG, "UTF16 decode failed, falling back to raw bytes"); + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize((const char *)elem_ptr, ind_len); + } + } + } else if (curr->svalue) { + LogMsg(DEBUG, "Processing SQL_BLOB/SQL_CLOB scalar parameter using svalue"); + pyVal = PyUnicode_Decode(curr->svalue, ind_len, "utf-8", "strict"); + if (!pyVal) { + LogMsg(DEBUG, "UTF-8 decode failed, falling back to raw bytes"); + PyErr_Clear(); + pyVal = PyBytes_FromStringAndSize(curr->svalue, ind_len); + } + } + if (pyVal && PyUnicode_Check(pyVal)) { + PyObject *trimmed = PyObject_CallMethod(pyVal, "rstrip", "s", " \x00"); + if (trimmed) { + Py_DECREF(pyVal); + pyVal = trimmed; + LogMsg(DEBUG, "SQL_BLOB/SQL_CLOB trimming succeeded"); + } else { + PyErr_Clear(); + } + } + snprintf(messageStr, sizeof(messageStr),"SQL_BLOB/SQL_CLOB scalar fetch exit: pyVal=%p", (void*)pyVal); + LogMsg(DEBUG, messageStr); + break; + } + + case SQL_BIGINT:{ + LogMsg(DEBUG, "Processing SQL_BIGINT scalar parameter"); + int64_t bigint_val = 0; + if (curr->svalue != NULL) { + bigint_val = strtoll(curr->svalue, NULL, 10); + } + snprintf(messageStr, sizeof(messageStr), "Scalar bigint_val = %lld", (long long)bigint_val); + LogMsg(DEBUG, messageStr); + pyVal = PyLong_FromLongLong(bigint_val); + break; + } + + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_DECFLOAT:{ + LogMsg(DEBUG, "Processing SQL_DECIMAL/SQL_NUMERIC/SQL_DECFLOAT scalar parameter"); + if (curr->svalue != NULL) { + char tempStr[128] = {0}; +#ifndef __MVS__ + strncpy(tempStr, curr->svalue, sizeof(tempStr) - 1); +#endif + tempStr[sizeof(tempStr) - 1] = '\0'; + + int len = strlen(tempStr); + while (len > 0 && tempStr[len - 1] == ' ') { + tempStr[--len] = '\0'; + } + char *dot = strchr(tempStr, '.'); + if (dot) { + char *end = tempStr + strlen(tempStr) - 1; + while (end > dot && *end == '0') { + *end-- = '\0'; + } + if (end == dot) *end = '\0'; + } + + PyObject *pyDecModule = PyImport_ImportModule("decimal"); + if (pyDecModule != NULL) { + PyObject *pyDecClass = PyObject_GetAttrString(pyDecModule, "Decimal"); + if (pyDecClass != NULL) { + PyObject *pyStr = PyUnicode_FromString(tempStr); + if (pyStr != NULL) { + Py_XDECREF(pyVal); + pyVal = PyObject_CallFunctionObjArgs(pyDecClass, pyStr, NULL); + Py_DECREF(pyStr); + } + Py_DECREF(pyDecClass); + } + Py_DECREF(pyDecModule); + } + } + break; + } + + case SQL_INTEGER: + case SQL_SMALLINT: + LogMsg(DEBUG, "Processing SQL_INTEGER/SQL_SMALLINT scalar parameter"); + snprintf(messageStr, sizeof(messageStr), "Scalar ivalue = %ld", curr->ivalue); + LogMsg(DEBUG, messageStr); + pyVal = PyLong_FromLong(curr->ivalue); + break; + + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: + LogMsg(DEBUG, "Processing SQL_REAL/SQL_FLOAT/SQL_DOUBLE scalar parameter"); + snprintf(messageStr, sizeof(messageStr), "Scalar fvalue = %f", curr->fvalue); + LogMsg(DEBUG, messageStr); + pyVal = PyFloat_FromDouble(curr->fvalue); + break; + + case SQL_TYPE_DATE: + LogMsg(DEBUG, "Processing SQL_TYPE_DATE scalar parameter"); + if (curr->date_value) { + snprintf(messageStr, sizeof(messageStr), + "Scalar date_value = %04d-%02d-%02d", + curr->date_value->year, + curr->date_value->month, + curr->date_value->day); + LogMsg(DEBUG, messageStr); + pyVal = PyDate_FromDate(curr->date_value->year, curr->date_value->month,curr->date_value->day); + } + break; + + case SQL_TYPE_TIME: + LogMsg(DEBUG, "Processing SQL_TYPE_TIME scalar parameter"); + if (curr->time_value) { + snprintf(messageStr, sizeof(messageStr),"Scalar time_value = %02d:%02d:%02d", + curr->time_value->hour % 24, + curr->time_value->minute, + curr->time_value->second); + LogMsg(DEBUG, messageStr); + pyVal = PyTime_FromTime(curr->time_value->hour % 24,curr->time_value->minute,curr->time_value->second, 0); + } + break; + + case SQL_TYPE_TIMESTAMP: + case SQL_TYPE_TIMESTAMP_WITH_TIMEZONE: + if (curr->uvalue) { + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP scalar parameter using uvalue"); + SQLWCHAR *ts_wstr = curr->uvalue; + SQLLEN ind_len = curr->bind_indicator; + PyObject *pyStr = getSQLWCharAsPyUnicodeObject(ts_wstr, ind_len); + if (!pyStr) { + PyErr_Clear(); + pyStr = PyBytes_FromStringAndSize((const char *)ts_wstr,ind_len * sizeof(SQLWCHAR)); + } + snprintf(messageStr, sizeof(messageStr),"Scalar TIMESTAMP parameter using uvalue, length=%ld",(long)ind_len); + LogMsg(DEBUG, messageStr); + const char *ts_str = PyUnicode_AsUTF8(pyStr); + if (ts_str) { + int year, month, day, hour, minute, second, micro = 0; + sscanf(ts_str, "%4d-%2d-%2d %2d:%2d:%2d.%6d",&year, &month, &day,&hour, &minute, &second, µ); + pyVal = PyDateTime_FromDateAndTime(year, month, day,hour, minute, second, micro); + Py_DECREF(pyStr); + } else { + pyVal = pyStr; + } + } + if (curr->ts_value) { + snprintf(messageStr, sizeof(messageStr), "Scalar timestamp_value = %04d-%02d-%02d %02d:%02d:%02d.%03d", + curr->ts_value->year, curr->ts_value->month, curr->ts_value->day, + curr->ts_value->hour % 24, curr->ts_value->minute, curr->ts_value->second, + (int)(curr->ts_value->fraction / 1000)); + LogMsg(DEBUG, messageStr); + pyVal = PyDateTime_FromDateAndTime( + curr->ts_value->year, + curr->ts_value->month, + curr->ts_value->day, + curr->ts_value->hour % 24, + curr->ts_value->minute, + curr->ts_value->second, + curr->ts_value->fraction / 1000); + } + if (curr->tstz_value){ + LogMsg(DEBUG, "Processing SQL_TYPE_TIMESTAMP_WITH_TIMEZONE scalar parameter using tstz_value"); + pyVal = format_timestamp_pystr(curr->tstz_value); + snprintf(messageStr, sizeof(messageStr), "Scalar timestamp_with_timezone = " + "%04d-%02d-%02d %02d:%02d:%02d.%03d %+03d:%02d", + curr->tstz_value->year, curr->tstz_value->month, curr->tstz_value->day, + curr->tstz_value->hour % 24, curr->tstz_value->minute, curr->tstz_value->second, + (int)(curr->tstz_value->fraction / 1000), + curr->tstz_value->timezone_hour, curr->tstz_value->timezone_minute); + LogMsg(DEBUG, messageStr); + } + break; + + default: + snprintf(messageStr, sizeof(messageStr),"Handling scalar parameter: unknown data_type=%d", curr->data_type); + LogMsg(WARNING, messageStr); + Py_INCREF(Py_None); + pyVal = Py_None; + snprintf(messageStr, sizeof(messageStr),"Returning Py_None for unsupported data_type=%d", curr->data_type); + LogMsg(DEBUG, messageStr); + break; + } + PyTuple_SET_ITEM(outTuple, idx++, pyVal); + } + } else { + snprintf(messageStr, sizeof(messageStr), "Parameter %d is NULL or no total", idx); + LogMsg(DEBUG, messageStr); + PyTuple_SET_ITEM(outTuple, idx++, pyVal); + } + curr = curr->next; + } + LogMsg(INFO, "exit ibm_db_fetch_callproc: returning output tuple"); + return outTuple; +} /* Listing of ibm_db module functions: */ static PyMethodDef ibm_db_Methods[] = { /* name, function, argument type, docstring */ @@ -17589,6 +18777,7 @@ static PyMethodDef ibm_db_Methods[] = { {"fetchone", (PyCFunction)ibm_db_fetchone, METH_VARARGS, "Fetch a single row from the result set."}, {"fetchall", (PyCFunction)ibm_db_fetchall, METH_VARARGS, "Fetch all rows from the result set."}, {"fetchmany", (PyCFunction)ibm_db_fetchmany, METH_VARARGS, "Fetch a specified number of rows from the result set."}, + {"fetch_callproc", (PyCFunction)ibm_db_fetch_callproc, METH_VARARGS, " Fetch the result set from stored procedure."}, /* An end-of-listing sentinel: */ {NULL, NULL, 0, NULL}}; diff --git a/ibm_db_tests/test_ArrayTypeAndSPCreation.py b/ibm_db_tests/test_ArrayTypeAndSPCreation.py new file mode 100644 index 00000000..faa2ffb4 --- /dev/null +++ b/ibm_db_tests/test_ArrayTypeAndSPCreation.py @@ -0,0 +1,591 @@ +# +# Licensed Materials - Property of IBM +# +# (c) Copyright IBM Corp. 2007-2026 +# + +from __future__ import print_function +import sys +import os +import unittest +import ibm_db +import config +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_ArrayTypeAndSPCreation(self): + obj = IbmDbTestFunctions() + obj.assert_expect(self.run_test_arraytypeandspcreation) + + def run_test_arraytypeandspcreation(self): + conn = ibm_db.connect(config.database, config.user, config.password) + server = ibm_db.server_info(conn) + + array_types = [ + ("int_array", "INTEGER"), + ("sint_array", "SMALLINT"), + ("bint_array", "BIGINT"), + ("float_array", "FLOAT"), + ("double_array", "DOUBLE"), + ("real_array", "REAL"), + ("decfloat16_array", "DECFLOAT(16)"), + ("decfloat34_array", "DECFLOAT(34)"), + ("decimal_array", "DECIMAL(10,2)"), + ("char_array", "CHAR(40)"), + ("vc_array", "VARCHAR(20)"), + ("vcfbd_array", "VARCHAR(20) FOR BIT DATA"), + ("date_array", "DATE"), + ("time_array", "TIME"), + ("ts_array", "TIMESTAMP"), + ("clob_array", "CLOB(500)"), + ("blob_array", "BLOB(500)") + ] + + # Create array types + for type_name, base_type in array_types: + try: + ibm_db.exec_immediate(conn, f"DROP TYPE {type_name}") + except: + pass + ibm_db.exec_immediate(conn, f"CREATE TYPE {type_name} AS {base_type} ARRAY[100]") + + proc_name = f"array_{type_name.replace('_array','')}12" + try: + ibm_db.exec_immediate(conn, f"DROP PROCEDURE {proc_name}") + except: + pass + + ibm_db.exec_immediate(conn, f"""CREATE PROCEDURE {proc_name}( IN var1 {type_name}, OUT var2 INTEGER) + LANGUAGE SQL + BEGIN + SET var2 = CARDINALITY(var1); + END""") + + + try: + ibm_db.exec_immediate(conn, "drop procedure array_bint22") + ibm_db.exec_immediate(conn, "drop procedure array_bint31") + ibm_db.exec_immediate(conn, "drop procedure array_bint41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_int22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_int31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_int41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_sint22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_sint31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_sint41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_real22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_real31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_real41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_float22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_float31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_float41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_double22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_double31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_double41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_dec22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_dec31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_dec41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_decflt1622") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_decflt1631") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_decflt1641") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_char22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_char31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_char41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vc22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vc31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vc41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vcfbd22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vcfbd31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_vcfbd41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_date22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_date31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_date41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_time22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_time31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_time41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_ts22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_ts31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_ts41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_clob22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_clob31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_clob41") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_blob22") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_blob31") + ibm_db.exec_immediate(conn, "DROP PROCEDURE array_blob41") + except: + pass + + ibm_db.exec_immediate(conn,"""create procedure array_int22( in var1 integer, out var2 int_array ) + LANGUAGE SQL \ + BEGIN \ + SET var2[1] = var1 * var1; \ + SET var2[2] = var1 * var1 * var1; \ + SET var2[3] = var1 * 4; \ + SET var2[4] = var1 - 5; \ + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_int31( INOUT var1 int_array) + LANGUAGE SQL + BEGIN + DECLARE var2 int_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) ORDER BY idx ASC + DO + SET var2[idx] = val + 1; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_int41(IN var1 int_array, OUT var2 int_array) + LANGUAGE SQL + BEGIN FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) ORDER BY idx ASC + DO + SET var2[idx] = val - 1; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_sint22(IN var1 SMALLINT, OUT var2 sint_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_sint31(INOUT var1 sint_array) + LANGUAGE SQL + BEGIN + DECLARE var2 sint_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_sint41( IN var1 sint_array, OUT var2 sint_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_bint22( IN var1 BIGINT, OUT var2 bint_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_bint31(INOUT var1 bint_array) + LANGUAGE SQL + BEGIN + DECLARE var2 bint_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_bint41(IN var1 bint_array,OUT var2 bint_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_real22( IN var1 REAL, OUT var2 real_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_real31(INOUT var1 real_array) + LANGUAGE SQL + BEGIN + DECLARE var2 real_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1.33; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_real41( IN var1 real_array, OUT var2 real_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1.67; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_float22(IN var1 FLOAT, OUT var2 float_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_float31(INOUT var1 float_array) + LANGUAGE SQL + BEGIN + DECLARE var2 float_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1.33; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """create procedure array_float41(in var1 float_array,out var2 float_array ) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) ORDER BY idx ASC + DO + SET var2[idx] = val - 1.67; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_double22(IN var1 DOUBLE, OUT var2 double_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_double31(INOUT var1 double_array) + LANGUAGE SQL + BEGIN + DECLARE var2 double_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1.33; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_double41(IN var1 double_array, OUT var2 double_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1.67; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_dec22(IN var1 DECIMAL(12,2), OUT var2 dec_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_dec31(INOUT var1 dec_array) + LANGUAGE SQL + BEGIN + DECLARE var2 dec_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1.33; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_dec41( IN var1 dec_array, OUT var2 dec_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1.67; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_decflt1622(IN var1 DECFLOAT(16), OUT var2 decfloat16_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 * var1; + SET var2[2] = var1 * var1 * var1; + SET var2[3] = var1 * 4; + SET var2[4] = var1 - 5; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_decflt1631(INOUT var1 decfloat16_array) + LANGUAGE SQL + BEGIN + DECLARE var2 decfloat16_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1.33; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_decflt1641(IN var1 decfloat16_array, OUT var2 decfloat16_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1.67; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_char22( IN var1 CHAR(40), OUT var2 char_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 || var1; + SET var2[2] = var1 || var1 || var1; + SET var2[3] = LEFT(var1,4); + SET var2[4] = RIGHT(var1,5); + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_char31(INOUT var1 char_array) + LANGUAGE SQL + BEGIN + DECLARE var2 char_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || LEFT(RTRIM(val),1); + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_char41(IN var1 char_array, OUT var2 char_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || RIGHT(RTRIM(val),1); + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vc22(IN var1 VARCHAR(20),OUT var2 vc_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 || var1; + SET var2[2] = var1 || var1 || var1; + SET var2[3] = LEFT(var1,4); + SET var2[4] = RIGHT(var1,5); + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vc31(INOUT var1 vc_array) + LANGUAGE SQL + BEGIN + DECLARE var2 vc_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || LEFT(RTRIM(val),1); + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vc41(IN var1 vc_array, OUT var2 vc_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || RIGHT(RTRIM(val),1); + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vcfbd22(IN var1 VARCHAR(20) FOR BIT DATA, OUT var2 vcfbd_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 || var1; + SET var2[2] = var1 || var1 || var1; + SET var2[3] = LEFT(var1,4); + SET var2[4] = RIGHT(var1,5); + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vcfbd31(INOUT var1 vcfbd_array) + LANGUAGE SQL + BEGIN + DECLARE var2 vcfbd_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || LEFT(RTRIM(val),1); + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_vcfbd41(IN var1 vcfbd_array, OUT var2 vcfbd_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = RTRIM(val) || ' - ' || RIGHT(RTRIM(val),1); + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_date22(IN var1 DATE, OUT var2 date_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 + 1 YEAR + 1 MONTH + 1 DAY; + SET var2[2] = var1 - 1 YEAR - 1 MONTH - 1 DAY; + SET var2[3] = var1 + 1 DAY; + SET var2[4] = var1 - 1 DAY; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_date31(INOUT var1 date_array) + LANGUAGE SQL + BEGIN + DECLARE var2 date_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1 YEAR - 1 MONTH + 1 DAY; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_date41(IN var1 date_array, OUT var2 date_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1 YEAR + 1 MONTH - 1 DAY; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_time22(IN var1 TIME, OUT var2 time_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 + 1 HOUR + 1 MINUTE + 1 SECOND; + SET var2[2] = var1 - 1 HOUR - 1 MINUTE - 1 SECOND; + SET var2[3] = var1 + 1 SECOND; + SET var2[4] = var1 - 1 SECOND; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_time31(INOUT var1 time_array) + LANGUAGE SQL + BEGIN + DECLARE var2 time_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1 HOUR + 1 MINUTE - 1 SECOND; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_time41(IN var1 time_array, OUT var2 time_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1 HOUR - 1 MINUTE + 1 SECOND; + END FOR; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_ts22(IN var1 TIMESTAMP, OUT var2 ts_array) + LANGUAGE SQL + BEGIN + SET var2[1] = var1 + 1 YEAR + 1 MONTH + 1 DAY + 1 HOUR + 1 MINUTE + 1 SECOND; + SET var2[2] = var1 - 1 YEAR - 1 MONTH - 1 DAY - 1 HOUR - 1 MINUTE - 1 SECOND; + SET var2[3] = var1 + 1 DAY + 1 SECOND; + SET var2[4] = var1 - 1 DAY - 1 SECOND; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_ts31(INOUT var1 ts_array) + LANGUAGE SQL + BEGIN + DECLARE var2 ts_array; + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val + 1 YEAR - 1 MONTH + 1 DAY - 1 HOUR + 1 MINUTE - 1 SECOND; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_ts41(IN var1 ts_array, OUT var2 ts_array) + LANGUAGE SQL + BEGIN + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[idx] = val - 1 YEAR + 1 MONTH - 1 DAY + 1 HOUR - 1 MINUTE + 1 SECOND; + END FOR; + END""") + + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_clob22(IN var1 INTEGER, OUT var2 clob_array) + LANGUAGE SQL + BEGIN + SET var2[1] = CLOB(CHAR(var1+1)); + SET var2[2] = CLOB(CHAR(var1+2)); + SET var2[3] = CLOB(CHAR(var1+3)); + SET var2[4] = CLOB(CHAR(var1+4)); + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_clob31(INOUT var1 clob_array) + LANGUAGE SQL + BEGIN + DECLARE var2 clob_array; + DECLARE var3 INTEGER; + SET var3 = CARDINALITY(var1); + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[var3-idx+1] = val; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_clob41(IN var1 clob_array, OUT var2 clob_array) + LANGUAGE SQL + BEGIN + SET var2 = var1; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_blob22(IN var1 INTEGER, OUT var2 blob_array) + LANGUAGE SQL + BEGIN + SET var2[1] = BLOB(CHAR(var1+1)); + SET var2[2] = BLOB(CHAR(var1+2)); + SET var2[3] = BLOB(CHAR(var1+3)); + SET var2[4] = BLOB(CHAR(var1+4)); + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_blob31(INOUT var1 blob_array) + LANGUAGE SQL + BEGIN + DECLARE var2 blob_array; + DECLARE var3 INTEGER; + SET var3 = CARDINALITY(var1); + FOR v AS SELECT val, idx FROM UNNEST(var1) WITH ORDINALITY AS T(val, idx) + DO + SET var2[var3-idx+1] = val; + END FOR; + SET var1 = var2; + END""") + + ibm_db.exec_immediate(conn, """CREATE PROCEDURE array_blob41(IN var1 blob_array, OUT var2 blob_array) + LANGUAGE SQL + BEGIN + SET var2 = var1; + END""") + + print("Preparation complete: array types and stored procedures created.") + +#__END__ +#__LUW_EXPECTED__ +#Preparation complete: array types and stored procedures created. +#__ZOS_EXPECTED__ +#Preparation complete: array types and stored procedures created. +#__SYSTEMI_EXPECTED__ +#Preparation complete: array types and stored procedures created. +#__IDS_EXPECTED__ +#Preparation complete: array types and stored procedures created. + diff --git a/ibm_db_tests/test_scalarsp_create.py b/ibm_db_tests/test_scalarsp_create.py new file mode 100644 index 00000000..23114c48 --- /dev/null +++ b/ibm_db_tests/test_scalarsp_create.py @@ -0,0 +1,70 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_scalarsp_create(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_scalarsp_create) + + def run_test_scalarsp_create(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + scalar_types = [ + ("int_scalar", "INTEGER"), + ("sint_scalar", "SMALLINT"), + ("bint_scalar", "BIGINT"), + ("float_scalar", "FLOAT"), + ("double_scalar", "DOUBLE"), + ("real_scalar", "REAL"), + ("decfloat16_scalar", "DECFLOAT(16)"), + ("decfloat34_scalar", "DECFLOAT(34)"), + ("decimal_scalar", "DECIMAL(10,2)"), + ("date_scalar", "DATE"), + ("time_scalar", "TIME"), + ("ts_scalar", "TIMESTAMP"), + ] + for proc_name, base_type in scalar_types: + try: + ibm_db.exec_immediate(conn, f"DROP PROCEDURE {proc_name}") + except: + pass + if base_type in ["INTEGER", "SMALLINT", "BIGINT"]: + operation = "SET var2 = var1 + 1;" + elif base_type in ["FLOAT", "DOUBLE", "REAL"]: + operation = "SET var2 = var1 + 1.0;" + elif base_type in ["DECFLOAT(16)", "DECFLOAT(34)", "DECIMAL(10,2)"]: + operation = "SET var2 = var1 + Decimal(1.25);" + elif base_type == "DATE": + operation = "SET var2 = var1 + 1 DAY;" + elif base_type == "TIME": + operation = "SET var2 = var1 + 1 MINUTE;" + elif base_type == "TIMESTAMP": + operation = "SET var2 = var1 + 1 SECOND;" + else: + operation = "SET var2 = var1;" + ibm_db.exec_immediate(conn, f""" + CREATE PROCEDURE {proc_name}(IN var1 {base_type}, OUT var2 {base_type}) + LANGUAGE SQL + BEGIN + {operation} + END + """) + print("Preparation complete: scalar stored procedures with safe operations created.") + +#__END__ +#__LUW_EXPECTED__ +#Preparation complete: scalar stored procedures with safe operations created. +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/ibm_db_tests/test_scalarsp_execute.py b/ibm_db_tests/test_scalarsp_execute.py new file mode 100644 index 00000000..6f04ef9c --- /dev/null +++ b/ibm_db_tests/test_scalarsp_execute.py @@ -0,0 +1,68 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_scalarsp_execute(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_scalarsp_execute) + + def run_test_scalarsp_execute(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + scalar_procs = [ + ("INTEGER", "int_scalar", 42), + ("SMALLINT", "sint_scalar", 7), + ("BIGINT", "bint_scalar", 1234), + ("FLOAT", "float_scalar", 3.14), + ("DOUBLE", "double_scalar", 20.25), + ("REAL", "real_scalar", 1.5), + ("DECFLOAT(16)", "decfloat16_scalar", 4.56), + ("DECFLOAT(34)", "decfloat34_scalar", 123456.1234), + ("DECIMAL(10,2)", "decimal_scalar", 56.78), + ("DATE", "date_scalar", date(2025, 1, 1)), + ("TIME", "time_scalar", time(12, 20, 30)), + ("TIMESTAMP", "ts_scalar", datetime(1989, 2, 12, 23, 55, 59)), + ] + + for base_type, proc_name, input_val in scalar_procs: + sql = f"CALL {proc_name}(?, ?)" + stmt = ibm_db.prepare(conn, sql) + output_val = input_val + + ibm_db.bind_param(stmt, 1, input_val, ibm_db.SQL_PARAM_INPUT) + ibm_db.bind_param(stmt, 2, output_val, ibm_db.SQL_PARAM_OUTPUT) + + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print(f"Procedure: {proc_name} {base_type} input: {result[1]} {base_type} output: {result[2]}") + + + +#__END__ +#__LUW_EXPECTED__ +#Procedure: int_scalar INTEGER input: 42 INTEGER output: 43 +#Procedure: sint_scalar SMALLINT input: 7 SMALLINT output: 8 +#Procedure: bint_scalar BIGINT input: 1234 BIGINT output: 1235 +#Procedure: float_scalar FLOAT input: 3.14 FLOAT output: 4.140000000000001 +#Procedure: double_scalar DOUBLE input: 20.25 DOUBLE output: 21.25 +#Procedure: real_scalar REAL input: 1.5 REAL output: 2.5 +#Procedure: decfloat16_scalar DECFLOAT(16) input: 4.56 DECFLOAT(16) output: 5.56 +#Procedure: decfloat34_scalar DECFLOAT(34) input: 123456.1234 DECFLOAT(34) output: 123457.1234 +#Procedure: decimal_scalar DECIMAL(10,2) input: 56.78 DECIMAL(10,2) output: 57.78 +#Procedure: date_scalar DATE input: 2025-01-01 DATE output: 2025-01-02 +#Procedure: time_scalar TIME input: 12:20:30 TIME output: 12:21:30 +#Procedure: ts_scalar TIMESTAMP input: 1989-02-12 23:55:59 TIMESTAMP output: 1989-02-12 23:56:00 +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/ibm_db_tests/test_sparray_cardinalities.py b/ibm_db_tests/test_sparray_cardinalities.py new file mode 100644 index 00000000..82250b0d --- /dev/null +++ b/ibm_db_tests/test_sparray_cardinalities.py @@ -0,0 +1,124 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_sparray_cardinalities(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_sparray_cardinalities) + + def run_test_sparray_cardinalities(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + array_procs = [ + ("int_array", "array_int12", [1, 2, 3, 4, 5, None]), + ("sint_array", "array_sint12", [7, 512, -29000, 32000]), + ("bint_array", "array_bint12", [1234567890123, None,-9876543210]), + ("float_array", "array_float12", [1.1, 2.2, 3.3]), + ("double_array", "array_double12", [10.5, 20.25, 30.75]), + ("real_array", "array_real12", [0.5, 1.5, 2.5]), + ("decfloat16_array", "array_decfloat1612", [1.23,None, 4.56, None]), + ("decfloat34_array", "array_decfloat3412", [1234567890.1234, None]), + ("decimal_array", "array_decimal12", [12.34, None, 56.78]), + ("time_array", "array_time12", [time(12, 20, 30), time(13, 30, 45)]), + ("date_array", "array_date12", [date(2025, 1, 1), date(2025, 12, 31)]), + ("ts_array", "array_ts12", [b'1981-07-08 10:42:34.000010',None, b'1982-07-08 10:42:34.000010']), + ("ts_array", "array_ts12", [ datetime(1989, 2, 12, 23, 55, 59, 342380), datetime(1990, 2, 12, 23, 55, 59, 342380)]), + ("char_array", "array_char12", ["abc", "defg", "jkl"]), + ("char_array", "array_char12", [b'abc', b'defg']), + ("vc_array", "array_vc12", ["hello", "world"]), + ("vc_array", "array_vc12", [b'hello', b'world']), + ("vcfbd_array", "array_vcfbd12",[b'abc', b'dog', b'deadbeef', None, b'foobar']), + ("clob_array", "array_clob12", [b'long text here', b'another clob']), + ("blob_array", "array_blob12", [b"binarydata", b"morebytes", None, b'abc']) + ] + for type_name, proc_name, input_array in array_procs: + sql = f"CALL {proc_name}(?,?)" + stmt = ibm_db.prepare(conn, sql) + output = 0 + + ibm_db.bind_param(stmt, 1, input_array, ibm_db.SQL_PARAM_INPUT) + ibm_db.bind_param(stmt, 2, output, ibm_db.SQL_PARAM_OUTPUT, ibm_db.SQL_INTEGER) + + ibm_db.execute(stmt) + inout_value = ibm_db.fetch_callproc(stmt) + + print(f"Procedure : {proc_name}") + print(f"{type_name} input:", inout_value[1]) + print(f"{type_name} cardinality:", inout_value[2]) + +#__END__ +#__LUW_EXPECTED__ +#Procedure : array_int12 +#int_array input: [1, 2, 3, 4, 5, None] +#int_array cardinality: 6 +#Procedure : array_sint12 +#sint_array input: [7, 512, -29000, 32000] +#sint_array cardinality: 4 +#Procedure : array_bint12 +#bint_array input: [1234567890123, None, -9876543210] +#bint_array cardinality: 3 +#Procedure : array_float12 +#float_array input: [1.1, 2.2, 3.3] +#float_array cardinality: 3 +#Procedure : array_double12 +#double_array input: [10.5, 20.25, 30.75] +#double_array cardinality: 3 +#Procedure : array_real12 +#real_array input: [0.5, 1.5, 2.5] +#real_array cardinality: 3 +#Procedure : array_decfloat1612 +#decfloat16_array input: [1.23, None, 4.56, None] +#decfloat16_array cardinality: 4 +#Procedure : array_decfloat3412 +#decfloat34_array input: [1234567890.1234, None] +#decfloat34_array cardinality: 2 +#Procedure : array_decimal12 +#decimal_array input: [12.34, None, 56.78] +#decimal_array cardinality: 3 +#Procedure : array_time12 +#time_array input: [datetime.time(12, 20, 30), datetime.time(13, 30, 45)] +#time_array cardinality: 2 +#Procedure : array_date12 +#date_array input: [datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] +#date_array cardinality: 2 +#Procedure : array_ts12 +#ts_array input: [b'1981-07-08 10:42:34.000010', None, b'1982-07-08 10:42:34.000010'] +#ts_array cardinality: 3 +#Procedure : array_ts12 +#ts_array input: [datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] +#ts_array cardinality: 2 +#Procedure : array_char12 +#char_array input: ['abc', 'defg', 'jkl'] +#char_array cardinality: 3 +#Procedure : array_char12 +#char_array input: [b'abc', b'defg'] +#char_array cardinality: 2 +#Procedure : array_vc12 +#vc_array input: ['hello', 'world'] +#vc_array cardinality: 2 +#Procedure : array_vc12 +#vc_array input: [b'hello', b'world'] +#vc_array cardinality: 2 +#Procedure : array_vcfbd12 +#vcfbd_array input: [b'abc', b'dog', b'deadbeef', None, b'foobar'] +#vcfbd_array cardinality: 5 +#Procedure : array_clob12 +#clob_array input: [b'long text here', b'another clob'] +#clob_array cardinality: 2 +#Procedure : array_blob12 +#blob_array input: [b'binarydata', b'morebytes', None, b'abc'] +#blob_array cardinality: 4 +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/ibm_db_tests/test_sparray_computations.py b/ibm_db_tests/test_sparray_computations.py new file mode 100644 index 00000000..9eb0ec0a --- /dev/null +++ b/ibm_db_tests/test_sparray_computations.py @@ -0,0 +1,105 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_sparray_computations(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_sparray_computations) + + def run_test_sparray_computations(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + scalar_procs = [ + ("int_array", "array_int22", 7), + ("sint_array", "array_sint22", 10), + ("bint_array", "array_bint22", 12345), + ("float_array", "array_float22", 3.14), + ("double_array", "array_double22", 20.25), + ("real_array", "array_real22", 1.5), + ("decfloat16_array", "array_decfloat1622", 4.56), + ("decfloat34_array", "array_decfloat3422", 123456.1234), + ("decimal_array", "array_decimal22", 56.78), + ("time_array", "array_time22", time(12, 20, 30)), + ("date_array", "array_date22", date(2025, 1, 1)), + ("ts_array", "array_ts22", datetime(1989, 2, 12, 23, 55, 59, 342380)), + ("char_array", "array_char22", b'HelloWorld'), + ("vc_array", "array_vc22", b'basketball'), + ("vcfbd_array", "array_vcfbd22", b'foobar'), + ] + for type_name, proc_name, input_val in scalar_procs: + sql = f"CALL {proc_name}(?, ?)" + stmt = ibm_db.prepare(conn, sql) + output_array = [input_val]*4 + + ibm_db.bind_param(stmt, 1, input_val, ibm_db.SQL_PARAM_INPUT) + ibm_db.bind_param(stmt, 2, output_array, ibm_db.SQL_PARAM_OUTPUT) + + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + ibm_db.debug(False) + + print(f"Procedure: {proc_name}") + print(f"{type_name} input:", input_val) + print(f"{type_name} output array:", result[2]) + +#__END__ +#__LUW_EXPECTED__ +#Procedure: array_int22 +#int_array input: 7 +#int_array output array: [49, 343, 28, 2] +#Procedure: array_sint22 +#sint_array input: 10 +#sint_array output array: [100, 1000, 40, 5] +#Procedure: array_bint22 +#bint_array input: 12345 +#bint_array output array: [152399025, 1881365963625, 49380, 12340] +#Procedure: array_float22 +#float_array input: 3.14 +#float_array output array: [9.8596, 30.959144000000002, 12.56, -1.8599999999999999] +#Procedure: array_double22 +#double_array input: 20.25 +#double_array output array: [410.0625, 8303.765625, 81.0, 15.25] +#Procedure: array_real22 +#real_array input: 1.5 +#real_array output array: [2.25, 3.375, 6.0, -3.5] +#Procedure: array_decfloat1622 +#decfloat16_array input: 4.56 +#decfloat16_array output array: [20.7936, 94.818816, 18.24, -0.44] +#Procedure: array_decfloat3422 +#decfloat34_array input: 123456.1234 +#decfloat34_array output array: [15241414404.956028, 1881645937568789.0, 493824.4936, 123451.1234] +#Procedure: array_decimal22 +#decimal_array input: 56.78 +#decimal_array output array: [3223.96, 183056.92, 227.12, 51.78] +#Procedure: array_time22 +#time_array input: 12:20:30 +#time_array output array: [datetime.time(13, 21, 31), datetime.time(11, 19, 29), datetime.time(12, 20, 31), datetime.time(12, 20, 29)] +#Procedure: array_date22 +#date_array input: 2025-01-01 +#date_array output array: [datetime.date(2026, 2, 2), datetime.date(2023, 11, 30), datetime.date(2025, 1, 2), datetime.date(2024, 12, 31)] +#Procedure: array_ts22 +#ts_array input: 1989-02-12 23:55:59.342380 +#ts_array output array: [datetime.datetime(1990, 3, 14, 0, 57, 0, 342380), datetime.datetime(1988, 1, 11, 22, 54, 58, 342380), datetime.datetime(1989, 2, 13, 23, 56, 0, 342380), datetime.datetime(1989, 2, 11, 23, 55, 58, 342380)] +#Procedure: array_char22 +#char_array input: b'HelloWorld' +#char_array output array: [b'HelloWorld ', b'HelloWorld ', b'Hell ', b' '] +#Procedure: array_vc22 +#vc_array input: b'basketball' +#vc_array output array: [b'basketballbasketbal', b'basketballbasketbal', b'bask', b'tball'] +#Procedure: array_vcfbd22 +#vcfbd_array input: b'foobar' +#vcfbd_array output array: [b'foobarfoobar', b'foobarfoobarfoobar', b'foobar', b'oobarr'] +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/ibm_db_tests/test_sparray_inout_computations.py b/ibm_db_tests/test_sparray_inout_computations.py new file mode 100644 index 00000000..5d9af000 --- /dev/null +++ b/ibm_db_tests/test_sparray_inout_computations.py @@ -0,0 +1,113 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_sparray_inout_computations(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_sparray_inout_computations) + + def run_test_sparray_inout_computations(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + array_procs = [ + ("int_array", "array_int31", [1, 2, 3, 4, 5]), # each element +1 + ("sint_array", "array_sint31", [7, 512, -29000, 32000]),# each element +1 + ("bint_array", "array_bint31", [1234567890123, None, -9876543210]), # each element +1 + ("float_array", "array_float31", [1.1, 2.2, 3.3]), # each element +1.33 + ("double_array", "array_double31", [10.5, 20.25, 30.75]), # each element +1.33 + ("real_array", "array_real31", [0.5, 1.5, 2.5]), # each element +1.33 + ("decfloat16_array", "array_decfloat1631", [1.23, None, 4.56, None]), # each element +1.33 + ("decfloat34_array", "array_decfloat3431", [12345678.1234, None]), # each element +1.33 + ("decimal_array", "array_decimal31", [12.34, None, 56.78]), # each element +1.33 + ("time_array", "array_time31", [time(12, 20, 30), time(13, 30, 45)]), # time manipulations + ("date_array", "array_date31", [date(2025, 1, 1), date(2025, 12, 31)]), # +1 YEAR -1 MONTH +1 DAY + ("ts_array", "array_ts31", [datetime(1989, 2, 12, 23, 55, 59, 342380), datetime(1990, 2, 12, 23, 55, 59, 342380)]), # timestamp manipulations + ("vc_array", "array_vc31", ["hello", "world"]), # same string transformation + ("vc_array", "array_vc31", [b'hello', b'world']), # same string transformation + ("vcfbd_array", "array_vcfbd31", [b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']), #unicode + ("clob_array", "array_clob31", [b'long text here', b'another clob']), + ("blob_array", "array_blob31", [b"binarydata", b"morebytes", b'abc']), + ("char_array", "array_char31", [b'abc', None,b'defg']), +] + + for type_name, proc_name, input_array in array_procs: + sql = f"CALL {proc_name}(?)" + stmt = ibm_db.prepare(conn, sql) + b1 = ibm_db.bind_param(stmt, 1, input_array, ibm_db.SQL_PARAM_INPUT_OUTPUT) + ibm_db.execute(stmt) + inout_value = ibm_db.fetch_callproc(stmt) + print(f"Procedure: {proc_name}") + print(f"{type_name} input:", input_array) + print(f"{type_name} output array:", inout_value[1]) + + +#__END__ +#__LUW_EXPECTED__ +#Procedure: array_int31 +#int_array input: [1, 2, 3, 4, 5] +#int_array output array: [2, 3, 4, 5, 6] +#Procedure: array_sint31 +#sint_array input: [7, 512, -29000, 32000] +#sint_array output array: [8, 513, -28999, 32001] +#Procedure: array_bint31 +#bint_array input: [1234567890123, None, -9876543210] +#bint_array output array: [1234567890124, None, -9876543209] +#Procedure: array_float31 +#float_array input: [1.1, 2.2, 3.3] +#float_array output array: [2.43, 3.5300000000000002, 4.63] +#Procedure: array_double31 +#double_array input: [10.5, 20.25, 30.75] +#double_array output array: [11.83, 21.58, 32.08] +#Procedure: array_real31 +#real_array input: [0.5, 1.5, 2.5] +#real_array output array: [1.8300000429153442, 2.8299999237060547, 3.8299999237060547] +#Procedure: array_decfloat1631 +#decfloat16_array input: [1.23, None, 4.56, None] +#decfloat16_array output array: [2.23, None, 5.56, None] +#Procedure: array_decfloat3431 +#decfloat34_array input: [12345678.1234, None] +#decfloat34_array output array: [12345679.1234, None] +#Procedure: array_decimal31 +#decimal_array input: [12.34, None, 56.78] +#decimal_array output array: [13.34, None, 57.78] +#Procedure: array_time31 +#time_array input: [datetime.time(12, 20, 30), datetime.time(13, 30, 45)] +#time_array output array: [datetime.time(11, 21, 29), datetime.time(12, 31, 44)] +#Procedure: array_date31 +#date_array input: [datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] +#date_array output array: [datetime.date(2025, 12, 2), datetime.date(2026, 12, 1)] +#Procedure: array_ts31 +#ts_array input: [datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] +#ts_array output array: [datetime.datetime(1990, 1, 13, 22, 56, 58, 342380), datetime.datetime(1991, 1, 13, 22, 56, 58, 342380)] +#Procedure: array_vc31 +#vc_array input: ['hello', 'world'] +#vc_array output array: ['hello - h', 'world - w'] +#Procedure: array_vc31 +#vc_array input: [b'hello', b'world'] +#vc_array output array: [b'hello - h', b'world - w'] +#Procedure: array_vcfbd31 +#vcfbd_array input: [b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse'] +#vcfbd_array output array: [b'basketball - b', b'baseball - b', b'football - f', b'pingpong - p', b'lacrosse - l'] +#Procedure: array_clob31 +#clob_array input: [b'long text here', b'another clob'] +#clob_array output array: [b'another clob', b'long text here'] +#Procedure: array_blob31 +#blob_array input: [b'binarydata', b'morebytes', b'abc'] +#blob_array output array: [b'abcarydata', b'morebytes', b'binarydata'] +#Procedure: array_char31 +#char_array input: [b'abc', None, b'defg'] +#char_array output array: [b'abc - a ', None, b'defg - d '] +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/ibm_db_tests/test_sparray_input_output_computations.py b/ibm_db_tests/test_sparray_input_output_computations.py new file mode 100644 index 00000000..4947daf5 --- /dev/null +++ b/ibm_db_tests/test_sparray_input_output_computations.py @@ -0,0 +1,105 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from datetime import date, time, datetime +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_sparray_input_output_computations(self): + obj = IbmDbTestFunctions() + obj.assert_expectf(self.run_test_sparray_input_output_computations) + + def run_test_sparray_input_output_computations(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + array_procs = [ + ("int_array", "array_int41", [1, 2, 3, 4, 5]), + ("sint_array", "array_sint41", [7, 512, -29000, 32000]), + ("bint_array", "array_bint41", [1234567890123, None, -9876543210]), + ("float_array", "array_float41", [1.1, 2.2, 3.3]), + ("double_array", "array_double41", [10.5, 20.25, 30.75]), + ("real_array", "array_real41", [0.5, 1.5, 2.5]), + ("decfloat16_array", "array_decfloat1641", [1.23, None, 4.56, None]), + ("decfloat34_array", "array_decfloat3441", [12345678.1234, None]), + ("decimal_array", "array_decimal41", [12.34, None, 56.78]), + ("time_array", "array_time41", [time(12, 20, 30), time(13, 30, 45)]), + ("date_array", "array_date41", [date(2025, 1, 1), date(2025, 12, 31)]), + ("ts_array", "array_ts41", [ datetime(1989, 2, 12, 23, 55, 59, 342380),datetime(1990, 2, 12, 23, 55, 59, 342380)]), + ("vc_array", "array_vc41", ["hello", "world"]), + ("vcfbd_array", "array_vcfbd41", [b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse']), + ("blob_array", "array_blob41", [b"binarydata", b"morebytes", None, b'abc']), + ] + for type_name, proc_name, input_array in array_procs: + sql = f"CALL {proc_name}(?, ?)" + stmt = ibm_db.prepare(conn, sql) + + ibm_db.bind_param(stmt, 1, input_array, ibm_db.SQL_PARAM_INPUT) + ibm_db.bind_param(stmt, 2, input_array, ibm_db.SQL_PARAM_OUTPUT) + + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + + print(f"Procedure: {proc_name}") + print(f"{proc_name} input array:", input_array) + print(f"{type_name} output array:", result[2]) + + + +#__END__ +#__LUW_EXPECTED__ +#Procedure: array_int41 +#array_int41 input array: [1, 2, 3, 4, 5] +#int_array output array: [0, 1, 2, 3, 4] +#Procedure: array_sint41 +#array_sint41 input array: [7, 512, -29000, 32000] +#sint_array output array: [6, 511, -29001, 31999] +#Procedure: array_bint41 +#array_bint41 input array: [1234567890123, None, -9876543210] +#bint_array output array: [1234567890122, None, -9876543211] +#Procedure: array_float41 +#array_float41 input array: [1.1, 2.2, 3.3] +#float_array output array: [-0.5699999999999998, 0.5300000000000002, 1.63] +#Procedure: array_double41 +#array_double41 input array: [10.5, 20.25, 30.75] +#double_array output array: [8.83, 18.58, 29.08] +#Procedure: array_real41 +#array_real41 input array: [0.5, 1.5, 2.5] +#real_array output array: [-1.1699999570846558, -0.17000000178813934, 0.8299999833106995] +#Procedure: array_decfloat1641 +#array_decfloat1641 input array: [1.23, None, 4.56, None] +#decfloat16_array output array: [0.23, None, 3.56, None] +#Procedure: array_decfloat3441 +#array_decfloat3441 input array: [12345678.1234, None] +#decfloat34_array output array: [12345677.1234, None] +#Procedure: array_decimal41 +#array_decimal41 input array: [12.34, None, 56.78] +#decimal_array output array: [11.34, None, 55.78] +#Procedure: array_time41 +#array_time41 input array: [datetime.time(12, 20, 30), datetime.time(13, 30, 45)] +#time_array output array: [datetime.time(13, 19, 31), datetime.time(14, 29, 46)] +#Procedure: array_date41 +#array_date41 input array: [datetime.date(2025, 1, 1), datetime.date(2025, 12, 31)] +#date_array output array: [datetime.date(2024, 1, 31), datetime.date(2025, 1, 30)] +#Procedure: array_ts41 +#array_ts41 input array: [datetime.datetime(1989, 2, 12, 23, 55, 59, 342380), datetime.datetime(1990, 2, 12, 23, 55, 59, 342380)] +#ts_array output array: [datetime.datetime(1988, 3, 12, 0, 55, 0, 342380), datetime.datetime(1989, 3, 12, 0, 55, 0, 342380)] +#Procedure: array_vc41 +#array_vc41 input array: ['hello', 'world'] +#vc_array output array: ['hello - o', 'world - d'] +#Procedure: array_vcfbd41 +#array_vcfbd41 input array: [b'basketball', b'baseball', b'football', b'pingpong', b'lacrosse'] +#vcfbd_array output array: [b'basketball - l', b'baseball - l', b'football - l', b'pingpong - g', b'lacrosse - e'] +#Procedure: array_blob41 +#array_blob41 input array: [b'binarydata', b'morebytes', None, b'abc'] +#blob_array output array: [b'binarydata', b'morebytes', None, b'abc'] +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... +