Implementation of SQLSetDescRec & SQLGetDescRec functions (#23)
authorHunaid Sohail <76044242+Hunaid2000@users.noreply.github.com>
Sun, 11 Aug 2024 23:30:48 +0000 (04:30 +0500)
committerGitHub <noreply@github.com>
Sun, 11 Aug 2024 23:30:48 +0000 (19:30 -0400)
* 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

12 files changed:
descriptor.c
descriptor.h
info.c
odbcapi30.c
odbcapi30w.c
pgapi30.c
pgapifunc.h
psqlodbc.def
test/expected/descrec.out [new file with mode: 0644]
test/expected/descrec_1.out [new file with mode: 0644]
test/src/descrec-test.c [new file with mode: 0644]
test/tests

index b473a4d99fda9a1087d70c0d4e66c11e5918fa33..319c37b1193a3f3f4d651652000585b9b0c5249f 100644 (file)
@@ -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)
index b15ee01f3420405360e9fa13c48d4caeefcbc54b..020db3bfdb810e00ce670b4695c6b0ef227535c1 100644 (file)
@@ -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 fb17316ad524f4a8df5ba61cfc1ef10672bdcb3b..a32f9884c1fc09f941683694bbcd9a09bf595f9e 100644 (file)
--- 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;
index 95e3a302a430f0adaf093ec8d12d853175cca29f..cdfd29cf573342e7d1d6d4bf676fb0502c05a3e5 100644 (file)
@@ -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 */
index 2c316f71bd0e764b02f4ed75d629ce194bf56fcb..ec21aeadc172e5733b75af142156ce57de7380c0 100644 (file)
@@ -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;
 }
index bee7e82cea97afa275f555c29c3794ebd1fcf44f..8f061d023202299539ec6fb37fcd2419b59c5961 100644 (file)
--- 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
index 8c9599cd947f806617c539c77e235471b637fa8f..d5146c802a1118472f7a2542d194dfbdba2dfef0 100644 (file)
@@ -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,
index 69fc9b8901907196d9feb2a7459da82887bf9d1e..36ac0dd319fa9e5166d5ee8f080cd10dccb7fb87 100644 (file)
@@ -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 (file)
index 0000000..69a71b3
--- /dev/null
@@ -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 (file)
index 0000000..0990bd0
--- /dev/null
@@ -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 (file)
index 0000000..6f37bf0
--- /dev/null
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#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;
+}
index bc99d32f9c07ecda4a09997cfcad5a23d178c220..4db4b81d9865077f19dc671ef22726c4a58414d0 100644 (file)
@@ -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