From 334247d466e814996138499016bc2eaf5f12bcc7 Mon Sep 17 00:00:00 2001 From: Hunaid Sohail <76044242+Hunaid2000@users.noreply.github.com> Date: Mon, 12 Aug 2024 04:30:48 +0500 Subject: [PATCH] Implementation of SQLSetDescRec & SQLGetDescRec functions (#23) * Implemented SQLSetDescRec function - Fixed SQL_DESC_PRECISION field in IPDSetField & IPDGetField functions - Added support for other fields for bookmark column in ARDSetField - SQL_DESC_TYPE - SQL_DESC_DATETIME_INTERVAL_CODE - SQL_DESC_OCTET_LENGTH - SQL_DESC_PRECISION - SQL_DESC_SCALE * Implemented SQLGetDescRec function - Handled SQL_NO_DATA case in GetField functions - Handled 01004 SQLSTATE in GetField functions - Added support for SQL_DESC_NAME & SQL_DESC_NULLABLE IPDGetField functions * Implemented SQLGetDescRecW & SQLSetDescRecW functions - Updated PGAPI_GetFunctions function to get new functions - Updated psqlodbc.def file for new functions on Windows - Fixed HY007 error handling * Added regression test for DescRec functions --- descriptor.c | 2 + descriptor.h | 2 + info.c | 4 +- odbcapi30.c | 32 ++-- odbcapi30w.c | 61 +++++++- pgapi30.c | 285 ++++++++++++++++++++++++++++++++++-- pgapifunc.h | 11 ++ psqlodbc.def | 4 +- test/expected/descrec.out | 34 +++++ test/expected/descrec_1.out | 34 +++++ test/src/descrec-test.c | 71 +++++++++ test/tests | 3 +- 12 files changed, 507 insertions(+), 36 deletions(-) create mode 100644 test/expected/descrec.out create mode 100644 test/expected/descrec_1.out create mode 100644 test/src/descrec-test.c diff --git a/descriptor.c b/descriptor.c index b473a4d..319c37b 100644 --- a/descriptor.c +++ b/descriptor.c @@ -701,6 +701,8 @@ static const struct { DESC_OPTION_NOT_FOR_THE_DRIVER, "HYC00", "HYC00" }, { DESC_FETCH_OUT_OF_RANGE, "HY106", "S1106" }, { DESC_COUNT_FIELD_INCORRECT, "07002", "07002" }, + { DESC_STATEMENT_NOT_PREPARED, "HY007", "S1010" }, + { DESC_STRING_DATA_TRUNCATED, "01004", "01004"} }; static PG_ErrorInfo *DC_create_errorinfo(const DescriptorClass *self) diff --git a/descriptor.h b/descriptor.h index b15ee01..020db3b 100644 --- a/descriptor.h +++ b/descriptor.h @@ -262,5 +262,7 @@ enum { ,DESC_OPTION_NOT_FOR_THE_DRIVER ,DESC_FETCH_OUT_OF_RANGE ,DESC_COUNT_FIELD_INCORRECT + ,DESC_STATEMENT_NOT_PREPARED + ,DESC_STRING_DATA_TRUNCATED }; #endif /* __DESCRIPTOR_H__ */ diff --git a/info.c b/info.c index fb17316..a32f988 100644 --- a/info.c +++ b/info.c @@ -1565,18 +1565,18 @@ PGAPI_GetFunctions(HDBC hdbc, case SQL_API_SQLFREEHANDLE: /* 1006 */ case SQL_API_SQLGETCONNECTATTR: /* 1007 */ case SQL_API_SQLGETDESCFIELD: /* 1008 */ + case SQL_API_SQLGETDESCREC: /* 1009 */ case SQL_API_SQLGETDIAGFIELD: /* 1010 */ case SQL_API_SQLGETDIAGREC: /* 1011 */ case SQL_API_SQLGETENVATTR: /* 1012 */ case SQL_API_SQLGETSTMTATTR: /* 1014 */ case SQL_API_SQLSETCONNECTATTR: /* 1016 */ case SQL_API_SQLSETDESCFIELD: /* 1017 */ + case SQL_API_SQLSETDESCREC: /* 1018 */ case SQL_API_SQLSETENVATTR: /* 1019 */ case SQL_API_SQLSETSTMTATTR: /* 1020 */ *pfExists = TRUE; break; - case SQL_API_SQLGETDESCREC: /* 1009 */ - case SQL_API_SQLSETDESCREC: /* 1018 */ case SQL_API_SQLCOPYDESC: /* 1004 */ *pfExists = FALSE; break; diff --git a/odbcapi30.c b/odbcapi30.c index 95e3a30..cdfd29c 100644 --- a/odbcapi30.c +++ b/odbcapi30.c @@ -308,9 +308,14 @@ SQLGetDescRec(SQLHDESC DescriptorHandle, SQLLEN *Length, SQLSMALLINT *Precision, SQLSMALLINT *Scale, SQLSMALLINT *Nullable) { - MYLOG(0, "Entering\n"); - MYLOG(0, "Error not implemented\n"); - return SQL_ERROR; + RETCODE ret; + + MYLOG(0, "Entering h=%p rec=%d name=%p blen=%d\n", DescriptorHandle, RecNumber, Name, BufferLength); + MYLOG(0, "str=%p type=%p sub=%p len=%p prec=%p scale=%p null=%p\n", StringLength, Type, SubType, Length, Precision, Scale, Nullable); + ret = PGAPI_GetDescRec(DescriptorHandle, RecNumber, Name, BufferLength, + StringLength, Type, SubType, Length, Precision, + Scale, Nullable); + return ret; } /* new function */ @@ -459,9 +464,14 @@ SQLSetDescRec(SQLHDESC DescriptorHandle, PTR Data, SQLLEN *StringLength, SQLLEN *Indicator) { - MYLOG(0, "Entering\n"); - MYLOG(0, "Error not implemented\n"); - return SQL_ERROR; + RETCODE ret; + + MYLOG(0, "Entering h=%p rec=%d type=%d sub=%d len=" FORMAT_LEN " prec=%d scale=%d data=%p\n", DescriptorHandle, RecNumber, Type, SubType, Length, Precision, Scale, Data); + MYLOG(0, "str=%p ind=%p\n", StringLength, Indicator); + ret = PGAPI_SetDescRec(DescriptorHandle, RecNumber, Type, + SubType, Length, Precision, Scale, Data, + StringLength, Indicator); + return ret; } #endif /* UNICODE_SUPPORTXX */ @@ -646,20 +656,14 @@ MYLOG(DETAIL_LOG_LEVEL, "lie=%d\n", ci->drivers.lie); SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEHANDLE); /* 1006 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTATTR); /* 1007 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCFIELD); /* 1008 */ - if (ci->drivers.lie) - { - SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCREC); /* 1009 not implemented yet */ - } + SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCREC); /* 1009 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGFIELD); /* 1010 minimal implementation */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGREC); /* 1011 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETENVATTR); /* 1012 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTATTR); /* 1014 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETCONNECTATTR); /* 1016 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCFIELD); /* 1017 */ - if (ci->drivers.lie) - { - SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCREC); /* 1018 not implemented yet */ - } + SQL_FUNC_ESET(pfExists, SQL_API_SQLSETDESCREC); /* 1018 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETENVATTR); /* 1019 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSTMTATTR); /* 1020 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLFETCHSCROLL); /* 1021 */ diff --git a/odbcapi30w.c b/odbcapi30w.c index 2c316f7..ec21aea 100644 --- a/odbcapi30w.c +++ b/odbcapi30w.c @@ -426,9 +426,37 @@ SQLGetDescRecW(SQLHDESC DescriptorHandle, SQLLEN *Length, SQLSMALLINT *Precision, SQLSMALLINT *Scale, SQLSMALLINT *Nullable) { - MYLOG(0, "Entering\n"); - MYLOG(0, "Error not implemented\n"); - return SQL_ERROR; + RETCODE ret; + char *NameA = NULL; + SQLSMALLINT nlen; + + MYLOG(0, "Entering h=%p rec=%d name=%p blen=%d\n", DescriptorHandle, RecNumber, Name, BufferLength); + MYLOG(0, "str=%p type=%p sub=%p len=%p prec=%p scale=%p null=%p\n", StringLength, Type, SubType, Length, Precision, Scale, Nullable); + + if (BufferLength > 0) + NameA = malloc(BufferLength); + + ret = PGAPI_GetDescRec(DescriptorHandle, RecNumber, (SQLCHAR *) NameA, BufferLength, + &nlen, Type, SubType, Length, Precision, + Scale, Nullable); + if (SQL_SUCCEEDED(ret)) + { + if (NameA && nlen <= BufferLength) + { + SQLULEN ulen = utf8_to_ucs2_lf(NameA, nlen, FALSE, Name, BufferLength, TRUE); + if (ulen == (SQLULEN) -1) + nlen = (SQLSMALLINT) locale_to_sqlwchar((SQLWCHAR *) Name, NameA, BufferLength, FALSE); + else + nlen = (SQLSMALLINT) ulen; + if (nlen >= BufferLength) + ret = SQL_SUCCESS_WITH_INFO; + } + if (StringLength) + *StringLength = nlen; + } + if (NameA) + free(NameA); + return ret; } /* new function */ @@ -440,7 +468,28 @@ SQLSetDescRecW(SQLHDESC DescriptorHandle, PTR Data, SQLLEN *StringLength, SQLLEN *Indicator) { - MYLOG(0, "Entering\n"); - MYLOG(0, "Error not implemented\n"); - return SQL_ERROR; + RETCODE ret; + SQLLEN vallen; + char *uval = NULL; + BOOL val_alloced = FALSE; + + MYLOG(0, "Entering h=%p rec=%d type=%d sub=%d len=" FORMAT_LEN " prec=%d scale=%d data=%p\n", DescriptorHandle, RecNumber, Type, SubType, Length, Precision, Scale, Data); + MYLOG(0, "str=%p ind=%p\n", StringLength, Indicator); + + if (Length > 0 || SQL_NTS == Length) + { + uval = ucs2_to_utf8(Data, Length > 0 ? Length / WCLEN : Length, &vallen, FALSE); + val_alloced = TRUE; + } + if (!val_alloced) + { + uval = Data; + vallen = Length; + } + ret = PGAPI_SetDescRec(DescriptorHandle, RecNumber, Type, + SubType, Length, Precision, Scale, uval, + &vallen, Indicator); + if (val_alloced) + free(uval); + return ret; } diff --git a/pgapi30.c b/pgapi30.c index bee7e82..8f061d0 100644 --- a/pgapi30.c +++ b/pgapi30.c @@ -565,6 +565,31 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, switch (FieldIdentifier) { + case SQL_DESC_TYPE: + bookmark->returntype = CAST_PTR(SQLSMALLINT, Value); + break; + case SQL_DESC_DATETIME_INTERVAL_CODE: + switch (bookmark->returntype) + { + case SQL_DATETIME: + case SQL_C_TYPE_DATE: + case SQL_C_TYPE_TIME: + case SQL_C_TYPE_TIMESTAMP: + switch ((LONG_PTR) Value) + { + case SQL_CODE_DATE: + bookmark->returntype = SQL_C_TYPE_DATE; + break; + case SQL_CODE_TIME: + bookmark->returntype = SQL_C_TYPE_TIME; + break; + case SQL_CODE_TIMESTAMP: + bookmark->returntype = SQL_C_TYPE_TIMESTAMP; + break; + } + break; + } + break; case SQL_DESC_DATA_PTR: bookmark->buffer = Value; break; @@ -574,6 +599,15 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_OCTET_LENGTH_PTR: bookmark->used = Value; break; + case SQL_DESC_OCTET_LENGTH: + bookmark->buflen = CAST_PTR(SQLLEN, Value); + break; + case SQL_DESC_PRECISION: + bookmark->precision = CAST_PTR(SQLSMALLINT, Value); + break; + case SQL_DESC_SCALE: + bookmark->scale = CAST_PTR(SQLSMALLINT, Value); + break; default: DC_set_error(desc, DESC_INVALID_COLUMN_NUMBER_ERROR, "invalid column number"); ret = SQL_ERROR; @@ -944,6 +978,20 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) case SQL_DESC_PARAMETER_TYPE: ipdopts->parameters[para_idx].paramType = CAST_PTR(SQLSMALLINT, Value); break; + case SQL_DESC_PRECISION: + switch (ipdopts->parameters[para_idx].SQLType) + { + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + case SQL_DATETIME: + ipdopts->parameters[para_idx].decimal_digits = CAST_PTR(SQLSMALLINT, Value); + break; + case SQL_NUMERIC: + ipdopts->parameters[para_idx].precision = CAST_PTR(SQLINTEGER, Value); + break; + } + break; case SQL_DESC_SCALE: ipdopts->parameters[para_idx].decimal_digits = CAST_PTR(SQLSMALLINT, Value); break; @@ -957,6 +1005,8 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) else NULL_THE_NAME(ipdopts->parameters[para_idx].paramName); break; + case SQL_DESC_OCTET_LENGTH: + break; case SQL_DESC_ALLOC_TYPE: /* read-only */ case SQL_DESC_CASE_SENSITIVE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: @@ -965,8 +1015,6 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ case SQL_DESC_NULLABLE: /* read-only */ case SQL_DESC_NUM_PREC_RADIX: - case SQL_DESC_OCTET_LENGTH: - case SQL_DESC_PRECISION: case SQL_DESC_ROWVER: /* read-only */ case SQL_DESC_TYPE_NAME: /* read-only */ case SQL_DESC_UNSIGNED: /* read-only */ @@ -1019,6 +1067,7 @@ ARDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, } switch (FieldIdentifier) { + case SQL_DESC_ALLOC_TYPE: case SQL_DESC_ARRAY_SIZE: case SQL_DESC_ARRAY_STATUS_PTR: case SQL_DESC_BIND_OFFSET_PTR: @@ -1026,12 +1075,15 @@ ARDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_COUNT: break; default: - if (RecNumber <= 0 || RecNumber > opts->allocated) + if (RecNumber <= 0) { DC_set_error(desc, DESC_INVALID_COLUMN_NUMBER_ERROR, "invalid column number"); return SQL_ERROR; } + /* RecNumber should be less than or equal to the number of descriptor records */ + else if (RecNumber > opts->allocated) + return SQL_NO_DATA; } row_idx = RecNumber - 1; switch (FieldIdentifier) @@ -1166,19 +1218,24 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, len = sizeof(SQLINTEGER); switch (FieldIdentifier) { + case SQL_DESC_ALLOC_TYPE: case SQL_DESC_ARRAY_SIZE: case SQL_DESC_ARRAY_STATUS_PTR: case SQL_DESC_BIND_OFFSET_PTR: case SQL_DESC_BIND_TYPE: case SQL_DESC_COUNT: break; - default:if (RecNumber <= 0 || RecNumber > opts->allocated) + default: + if (RecNumber <= 0) { -MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, opts->allocated); + MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, opts->allocated); DC_set_error(desc, DESC_BAD_PARAMETER_NUMBER_ERROR, "bad parameter number"); return SQL_ERROR; } + /* RecNumber should be less than or equal to the number of descriptor records */ + else if (RecNumber > opts->allocated) + return SQL_NO_DATA; } para_idx = RecNumber - 1; switch (FieldIdentifier) @@ -1376,6 +1433,16 @@ IRDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, StatementClass *stmt; stmt = opts->stmt; + /* if statement is in the prepared or executed state but there is no open cursor, return SQL_NO_DATA */ + if ((stmt->prepared >= PREPARED_PERMANENTLY || stmt->status == STMT_FINISHED) && NAME_IS_NULL(stmt->cursor_name)) + return SQL_NO_DATA; + /* HY007: statement handle had not been prepared or executed */ + if (SC_get_Curres(stmt) == NULL && stmt->prepared == NOT_YET_PREPARED) + { + DC_set_error(desc, DESC_STATEMENT_NOT_PREPARED, + "associated statement is not prepared"); + return SQL_ERROR; + } ret = PGAPI_ColAttributes(stmt, RecNumber, FieldIdentifier, Value, (SQLSMALLINT) BufferLength, &pcbL, &ival); @@ -1406,6 +1473,9 @@ IRDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, if (StringLength) *StringLength = len; + /* 01004: String data, right-truncated */ + if (ret == SQL_SUCCESS_WITH_INFO) + DC_set_error(desc, DESC_STRING_DATA_TRUNCATED, "string data truncated"); return ret; } @@ -1422,17 +1492,22 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, switch (FieldIdentifier) { + case SQL_DESC_ALLOC_TYPE: case SQL_DESC_ARRAY_STATUS_PTR: case SQL_DESC_ROWS_PROCESSED_PTR: case SQL_DESC_COUNT: break; - default:if (RecNumber <= 0 || RecNumber > ipdopts->allocated) + default: + if (RecNumber <= 0) { -MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated); + MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated); DC_set_error(desc, DESC_BAD_PARAMETER_NUMBER_ERROR, "bad parameter number"); return SQL_ERROR; } + /* RecNumber should be less than or equal to the number of descriptor records */ + else if (RecNumber > ipdopts->allocated) + return SQL_NO_DATA; } para_idx = RecNumber - 1; switch (FieldIdentifier) @@ -1501,6 +1576,9 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) case SQL_DATETIME: ival = ipdopts->parameters[para_idx].decimal_digits; break; + case SQL_NUMERIC: + ival = ipdopts->parameters[para_idx].precision; + break; } break; case SQL_DESC_SCALE: @@ -1512,19 +1590,39 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) break; } break; + case SQL_DESC_OCTET_LENGTH: + break; case SQL_DESC_ALLOC_TYPE: /* read-only */ rettype = SQL_IS_SMALLINT; ival = SQL_DESC_ALLOC_AUTO; break; + case SQL_DESC_NAME: + rettype = SQL_NTS; + if (NAME_IS_NULL(ipdopts->parameters[para_idx].paramName)) + ptr = (char *) SAFE_NAME(ipdopts->parameters[para_idx].paramName); + else + ptr = GET_NAME(ipdopts->parameters[para_idx].paramName); + if (ptr) + { + len = (SQLINTEGER) strlen(ptr); + if (Value) + { + strncpy_null((char *) Value, ptr, BufferLength); + if (len >= BufferLength) + ret = SQL_SUCCESS_WITH_INFO; + } + } + break; + case SQL_DESC_NULLABLE: /* read-only */ + rettype = SQL_IS_SMALLINT; + ival = SQL_NULLABLE; + break; case SQL_DESC_CASE_SENSITIVE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ case SQL_DESC_LENGTH: case SQL_DESC_LOCAL_TYPE_NAME: /* read-only */ - case SQL_DESC_NAME: - case SQL_DESC_NULLABLE: /* read-only */ case SQL_DESC_NUM_PREC_RADIX: - case SQL_DESC_OCTET_LENGTH: case SQL_DESC_ROWVER: /* read-only */ case SQL_DESC_TYPE_NAME: /* read-only */ case SQL_DESC_UNSIGNED: /* read-only */ @@ -1547,10 +1645,15 @@ MYLOG(DETAIL_LOG_LEVEL, "RecN=%d allocated=%d\n", RecNumber, ipdopts->allocated) len = sizeof(SQLPOINTER); *((void **)Value) = ptr; break; + case SQL_NTS: + break; } if (StringLength) *StringLength = len; + /* 01004: String data, right-truncated */ + if (ret == SQL_SUCCESS_WITH_INFO) + DC_set_error(desc, DESC_STRING_DATA_TRUNCATED, "string data truncated"); return ret; } @@ -1924,6 +2027,166 @@ PGAPI_SetDescField(SQLHDESC DescriptorHandle, return ret; } +/* new function */ +RETCODE SQL_API +PGAPI_SetDescRec(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLSMALLINT Type, + SQLSMALLINT SubType, SQLLEN Length, + SQLSMALLINT Precision, SQLSMALLINT Scale, + PTR Data, SQLLEN *StringLength, SQLLEN *Indicator) +{ + CSTR func = "PGAPI_SetDescRec"; + RETCODE ret = SQL_SUCCESS; + DescriptorClass *desc = (DescriptorClass *) DescriptorHandle; + + MYLOG(0, "entering h=%p(%d) rec=" FORMAT_SMALLI " type=" FORMAT_SMALLI " sub=" FORMAT_SMALLI " len=" FORMAT_LEN " prec=" FORMAT_SMALLI " scale=" FORMAT_SMALLI " data=%p\n", + DescriptorHandle, DC_get_desc_type(desc), RecNumber, Type, SubType, Length, Precision, Scale, Data); + MYLOG(0, "str=%p ind=%p\n", StringLength, Indicator); + + /* Descriptor handle must not be an IRD handle */ + if (DC_get_desc_type(desc) == SQL_ATTR_IMP_ROW_DESC) + { + DC_set_error(desc, DESC_INVALID_DESCRIPTOR_IDENTIFIER, "Invalid descriptor identifier"); + DC_log_error(func, "", desc); + return SQL_ERROR; + } + + /* + Set following descriptor fields: + + - SQL_DESC_TYPE + - SQL_DESC_DATETIME_INTERVAL_CODE + - SQL_DESC_OCTET_LENGTH + - SQL_DESC_PRECISION + - SQL_DESC_SCALE + - SQL_DESC_DATA_PTR + - SQL_DESC_OCTET_LENGTH_PTR + - SQL_DESC_INDICATOR_PTR + */ + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_TYPE, &Type, 0); + if (ret != SQL_SUCCESS) return ret; + + /* If Type is SQL_DATETIME or SQL_INTERVAL, the value of SQL_DESC_DATETIME_INTERVAL_CODE is set to SubType. */ + if (Type == SQL_DATETIME || Type == SQL_INTERVAL) { + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_DATETIME_INTERVAL_CODE, &SubType, 0); + if (ret != SQL_SUCCESS) return ret; + } + + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_OCTET_LENGTH, &Length, 0); + if (ret != SQL_SUCCESS) return ret; + + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_PRECISION, &Precision, 0); + if (ret != SQL_SUCCESS) return ret; + + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_SCALE, &Scale, 0); + if (ret != SQL_SUCCESS) return ret; + + /* SQL_DESC_DATA_PTR is only for ARD or APD */ + if (DC_get_desc_type(desc) != SQL_ATTR_IMP_PARAM_DESC) + { + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_DATA_PTR, &Data, 0); + if (ret != SQL_SUCCESS) return ret; + } + + /* SQL_DESC_OCTET_LENGTH_PTR is only for ARD or APD */ + if (DC_get_desc_type(desc) != SQL_ATTR_IMP_PARAM_DESC) + { + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_OCTET_LENGTH_PTR, StringLength, 0); + if (ret != SQL_SUCCESS) return ret; + } + + /* SQL_DESC_INDICATOR_PTR is only for ARD or APD */ + if (DC_get_desc_type(desc) != SQL_ATTR_IMP_PARAM_DESC) + { + ret = PGAPI_SetDescField(DescriptorHandle, RecNumber, SQL_DESC_INDICATOR_PTR, Indicator, 0); + if (ret != SQL_SUCCESS) return ret; + } + + return SQL_SUCCESS; +} + +/* new function */ +RETCODE SQL_API +PGAPI_GetDescRec(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLCHAR *Name, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLength, + SQLSMALLINT *Type, SQLSMALLINT *SubType, + SQLLEN *Length, SQLSMALLINT *Precision, + SQLSMALLINT *Scale, SQLSMALLINT *Nullable) +{ + RETCODE ret = SQL_SUCCESS; + DescriptorClass *desc = (DescriptorClass *) DescriptorHandle; + SQLSMALLINT strlen, typ, subtyp, prec, scal, null; + SQLLEN len; + + MYLOG(0, "entering h=%p(%d) rec=" FORMAT_SMALLI " name=%p blen=" FORMAT_SMALLI "\n", DescriptorHandle, DC_get_desc_type(desc), RecNumber, Name, BufferLength); + MYLOG(0, "str=%p type=%p sub=%p len=%p prec=%p scale=%p null=%p\n", StringLength, Type, SubType, Length, Precision, Scale, Nullable); + + /* + Get following descriptor fields: + + - SQL_DESC_TYPE + - SQL_DESC_DATETIME_INTERVAL_CODE + - SQL_DESC_OCTET_LENGTH + - SQL_DESC_PRECISION + - SQL_DESC_SCALE + - SQL_DESC_NULLABLE + - SQL_DESC_NAME + */ + + if (Type != NULL) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_TYPE, &typ, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *Type = typ; + } + + if (SubType != NULL && (typ == SQL_DATETIME || typ == SQL_INTERVAL)) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_DATETIME_INTERVAL_CODE, &subtyp, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *SubType = subtyp; + } + + if (Length != NULL) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_OCTET_LENGTH, &len, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *Length = (SQLLEN) len; + } + + if (Precision != NULL) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_PRECISION, &prec, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *Precision = prec; + } + + if (Scale != NULL) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_SCALE, &scal, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *Scale = scal; + } + + if (Nullable != NULL && (DC_get_desc_type(desc) == SQL_ATTR_IMP_ROW_DESC || DC_get_desc_type(desc) == SQL_ATTR_IMP_PARAM_DESC)) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_NULLABLE, &null, 0, NULL); + if (ret != SQL_SUCCESS) return ret; + *Nullable = null; + } + + if (Name != NULL && (DC_get_desc_type(desc) == SQL_ATTR_IMP_ROW_DESC || DC_get_desc_type(desc) == SQL_ATTR_IMP_PARAM_DESC)) + { + ret = PGAPI_GetDescField(DescriptorHandle, RecNumber, SQL_DESC_NAME, Name, BufferLength, (SQLINTEGER *) &strlen); + if (ret != SQL_SUCCESS) return ret; + if (StringLength != NULL) + *StringLength = strlen; + } + + return SQL_SUCCESS; +} + /* SQLSet(Param/Scroll/Stmt)Option -> SQLSetStmtAttr */ RETCODE SQL_API PGAPI_SetStmtAttr(HSTMT StatementHandle, @@ -2146,4 +2409,4 @@ PGAPI_BulkOperations(HSTMT hstmt, SQLSMALLINT operationX) ret = bulk_ope_callback(SQL_SUCCESS, &s); } return ret; -} +} \ No newline at end of file diff --git a/pgapifunc.h b/pgapifunc.h index 8c9599c..d5146c8 100644 --- a/pgapifunc.h +++ b/pgapifunc.h @@ -337,6 +337,17 @@ RETCODE SQL_API PGAPI_SetDescField(SQLHDESC DescriptorHandle, RETCODE SQL_API PGAPI_GetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength, SQLINTEGER *StringLength); +RETCODE SQL_API PGAPI_SetDescRec(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLSMALLINT Type, + SQLSMALLINT SubType, SQLLEN Length, + SQLSMALLINT Precision, SQLSMALLINT Scale, + PTR Data, SQLLEN *StringLength, SQLLEN *Indicator); +RETCODE SQL_API PGAPI_GetDescRec(SQLHDESC DescriptorHandle, + SQLSMALLINT RecNumber, SQLCHAR *Name, + SQLSMALLINT BufferLength, SQLSMALLINT *StringLength, + SQLSMALLINT *Type, SQLSMALLINT *SubType, + SQLLEN *Length, SQLSMALLINT *Precision, + SQLSMALLINT *Scale, SQLSMALLINT *Nullable); RETCODE SQL_API PGAPI_DescError(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR *MessageText, SQLSMALLINT BufferLength, diff --git a/psqlodbc.def b/psqlodbc.def index 69fc9b8..36ac0dd 100644 --- a/psqlodbc.def +++ b/psqlodbc.def @@ -60,7 +60,7 @@ SQLEndTran @85 SQLFetchScroll @86 SQLFreeHandle @87 ;SQLGetDescField @88 -;SQLGetDescRec @89 +SQLGetDescRec @89 ;SQLGetDiagField @90 ;SQLGetDiagRec @91 SQLGetEnvAttr @92 @@ -68,7 +68,7 @@ SQLGetEnvAttr @92 ;SQLGetStmtAttr @94 ;SQLSetConnectAttr @95 ;SQLSetDescField @96 -;SQLSetDescRec @97 +SQLSetDescRec @97 SQLSetEnvAttr @98 ;SQLSetStmtAttr @99 SQLBulkOperations @100 diff --git a/test/expected/descrec.out b/test/expected/descrec.out new file mode 100644 index 0000000..69a71b3 --- /dev/null +++ b/test/expected/descrec.out @@ -0,0 +1,34 @@ +connected + +-- Column 1 -- +SQL_DESC_NAME: col1 +SQL_DESC_TYPE: 4 +SQL_DESC_OCTET_LENGTH: 0 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 + +-- Column 2 -- +SQL_DESC_NAME: col2 +SQL_DESC_TYPE: 2 +SQL_DESC_OCTET_LENGTH: 6 +SQL_DESC_PRECISION: 4 +SQL_DESC_SCALE: 2 +SQL_DESC_NULLABLE: 1 + +-- Column 3 -- +SQL_DESC_NAME: col3 +SQL_DESC_TYPE: 12 +SQL_DESC_OCTET_LENGTH: 40 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 + +-- Column 4 -- +SQL_DESC_NAME: col4 +SQL_DESC_TYPE: -5 +SQL_DESC_OCTET_LENGTH: 8 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 +disconnecting diff --git a/test/expected/descrec_1.out b/test/expected/descrec_1.out new file mode 100644 index 0000000..0990bd0 --- /dev/null +++ b/test/expected/descrec_1.out @@ -0,0 +1,34 @@ +connected + +-- Column 1 -- +SQL_DESC_NAME: col1 +SQL_DESC_TYPE: 4 +SQL_DESC_OCTET_LENGTH: 0 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 + +-- Column 2 -- +SQL_DESC_NAME: col2 +SQL_DESC_TYPE: 2 +SQL_DESC_OCTET_LENGTH: 6 +SQL_DESC_PRECISION: 4 +SQL_DESC_SCALE: 2 +SQL_DESC_NULLABLE: 1 + +-- Column 3 -- +SQL_DESC_NAME: col3 +SQL_DESC_TYPE: 12 +SQL_DESC_OCTET_LENGTH: 20 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 + +-- Column 4 -- +SQL_DESC_NAME: col4 +SQL_DESC_TYPE: -5 +SQL_DESC_OCTET_LENGTH: 8 +SQL_DESC_PRECISION: 0 +SQL_DESC_SCALE: 0 +SQL_DESC_NULLABLE: 0 +disconnecting diff --git a/test/src/descrec-test.c b/test/src/descrec-test.c new file mode 100644 index 0000000..6f37bf0 --- /dev/null +++ b/test/src/descrec-test.c @@ -0,0 +1,71 @@ +#include +#include + +#include "common.h" + +int main(int argc, char **argv) +{ + SQLRETURN rc; + HSTMT hstmt = SQL_NULL_HSTMT; + SQLSMALLINT colcount; + SQLHDESC hDesc; + SQLCHAR name[64]; + SQLSMALLINT nameLen = 64; + SQLSMALLINT type, subType; + SQLSMALLINT precision, scale, nullable; + SQLLEN length; + + test_connect(); + + rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); + if (!SQL_SUCCEEDED(rc)) + { + print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn); + exit(1); + } + + /* Prepare a test table */ + rc = SQLExecDirect(hstmt, (SQLCHAR *) "CREATE TEMPORARY TABLE desctable (col1 int4 not null, col2 numeric(4,2), col3 varchar(10) not null, col4 bigint not null)", SQL_NTS); + CHECK_STMT_RESULT(rc, "SQLExecDirect failed while creating temp table", hstmt); + + /* Prepare a statement */ + rc = SQLPrepare(hstmt, (SQLCHAR *) "SELECT * FROM desctable", SQL_NTS); + CHECK_STMT_RESULT(rc, "SQLPrepare failed", hstmt); + + /* Execute */ + rc = SQLExecute(hstmt); + CHECK_STMT_RESULT(rc, "SQLExecute failed", hstmt); + + rc = SQLNumResultCols(hstmt, &colcount); + CHECK_STMT_RESULT(rc, "SQLNumResultCols failed", hstmt); + + /* Get the descriptor handle */ + rc = SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_ROW_DESC, &hDesc, 0, NULL); + CHECK_STMT_RESULT(rc, "SQLGetStmtAttr failed", hstmt); + + for (int i = 1; i <= colcount; i++) + { + rc = SQLGetDescRec(hDesc, i, name, nameLen, NULL, &type, &subType, &length, &precision, &scale, &nullable); + if (!SQL_SUCCEEDED(rc)) + { + print_diag("SQLGetDescRec failed", SQL_HANDLE_DESC, hDesc); + exit(1); + } + + printf("\n-- Column %d --\n", i); + printf("SQL_DESC_NAME: %s\n", name); + printf("SQL_DESC_TYPE: %d\n", type); + printf("SQL_DESC_OCTET_LENGTH: %d\n", (int) length); + printf("SQL_DESC_PRECISION: %d\n", precision); + printf("SQL_DESC_SCALE: %d\n", scale); + printf("SQL_DESC_NULLABLE: %d\n", nullable); + } + + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt); + + /* Clean up */ + test_disconnect(); + + return 0; +} diff --git a/test/tests b/test/tests index bc99d32..4db4b81 100644 --- a/test/tests +++ b/test/tests @@ -53,4 +53,5 @@ TESTBINS = exe/connect-test \ exe/odbc-escapes-test \ exe/wchar-char-test \ exe/params-batch-exec-test \ - exe/fetch-refcursors-test + exe/fetch-refcursors-test \ + exe/descrec-test -- 2.39.5