LCOV - code coverage report
Current view: top level - src/odbc/unittests - tvp.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 263 274 96.0 %
Date: 2025-02-21 09:36:06 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /* Test binding and calling of TVPs */
       2             : 
       3             : #include "common.h"
       4             : #include <assert.h>
       5             : #include <odbcss.h>
       6             : 
       7             : #undef MEMORY_TESTS
       8             : #if defined(HAVE_MALLOC_H)
       9             : #  include <malloc.h>
      10             : #  if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO) || defined(HAVE__HEAPWALK)
      11             : #    define MEMORY_TESTS 1
      12             : #  endif
      13             : #endif
      14             : 
      15             : #if defined(HAVE_VALGRIND_MEMCHECK_H)
      16             : #  include <valgrind/valgrind.h>
      17             : #else
      18             : #  define RUNNING_ON_VALGRIND 0
      19             : #endif
      20             : 
      21             : #include <freetds/bool.h>
      22             : 
      23             : #define MAX_ROWS 5
      24             : #define MAX_STRING_LENGTH 20
      25             : 
      26             : static SQLINTEGER intCol[MAX_ROWS];
      27             : static SQLCHAR strCol[MAX_ROWS][MAX_STRING_LENGTH], binCol[MAX_ROWS][MAX_STRING_LENGTH];
      28             : static SQL_DATE_STRUCT dateCol[MAX_ROWS];
      29             : static SQL_NUMERIC_STRUCT numericCol[MAX_ROWS];
      30             : 
      31             : static SQLLEN lIntCol[MAX_ROWS];
      32             : static SQLLEN lStrCol[MAX_ROWS];
      33             : static SQLLEN lDateCol[MAX_ROWS];
      34             : static SQLLEN lNumericCol[MAX_ROWS];
      35             : static SQLLEN lBinCol[MAX_ROWS];
      36             : 
      37             : static SQLCHAR outputBuffer[256];
      38             : static SQLLEN lenBuffer;
      39             : 
      40             : typedef union {
      41             :         SQLPOINTER fldSQLPOINTER;
      42             :         SQLSMALLINT fldSQLSMALLINT;
      43             :         SQLUSMALLINT fldSQLUSMALLINT;
      44             :         SQLINTEGER fldSQLINTEGER;
      45             :         SQLUINTEGER fldSQLUINTEGER;
      46             :         SQLLEN fldSQLLEN;
      47             :         SQLULEN fldSQLULEN;
      48             : } field_output;
      49             : 
      50             : /* utility to get a field from descriptor */
      51             : static field_output
      52         108 : get_desc_field(SQLINTEGER desc_type, SQLSMALLINT icol, SQLSMALLINT fDescType, size_t size)
      53             : {
      54             :         SQLHDESC desc;
      55             :         SQLINTEGER ind;
      56             :         field_output buf;
      57             : 
      58         108 :         assert(size <= sizeof(buf));
      59         108 :         CHKGetStmtAttr(desc_type, &desc, sizeof(desc), &ind, "S");
      60             : 
      61         108 :         memset(&buf, 0x5a, sizeof(buf));
      62         108 :         ind = 1234;
      63         108 :         CHKGetDescField(desc, icol, fDescType, &buf, size, &ind, "S");
      64         108 :         assert(ind == size);
      65             : 
      66         108 :         return buf;
      67             : }
      68             : 
      69             : /* Utility to get a field from descriptor.
      70             :  * desc_type   APP or IMP.
      71             :  * col         column number (or 0 if does not matter).
      72             :  * field       descriptor field (SQL_DESC_xxx).
      73             :  * type        SQL type to be returned, strings not supported.
      74             :  */
      75             : #define GET_DESC_FIELD(desc_type, col, field, type) \
      76             :         (get_desc_field(SQL_ATTR_ ## desc_type ## _PARAM_DESC, col, field, sizeof(type)).fld ## type)
      77             : 
      78             : /* utility to check condition and returns error string */
      79             : static char*
      80         104 : check_cond(bool condition, const char *fmt, ...)
      81             : {
      82             :         va_list ap;
      83         104 :         char *ret = NULL;
      84             : 
      85         104 :         if (condition)
      86             :                 return ret;
      87             : 
      88           0 :         va_start(ap, fmt);
      89           0 :         assert(vasprintf(&ret, fmt, ap) >= 0);
      90           0 :         va_end(ap);
      91           0 :         return ret;
      92             : }
      93             : 
      94             : #define CHECK_COND(args) do { \
      95             :         char *err = check_cond args; \
      96             :         if (err) { \
      97             :                 failed = true; \
      98             :                 fprintf(stderr, "Wrong condition at line %d: %s\n", __LINE__, err); \
      99             :                 free(err); \
     100             :         } \
     101             : } while(0)
     102             : 
     103             : /*
     104             :  * Generate some data as the columns of our TVPs
     105             :  */
     106             : static void
     107          10 : setup(void)
     108             : {
     109             :         int i;
     110             : 
     111          60 :         for (i = 0; i < MAX_ROWS; i++) {
     112             :                 /* Setup integer column */
     113          50 :                 intCol[i] = i * 10;
     114          50 :                 lIntCol[i] = sizeof(SQLINTEGER);
     115             : 
     116             :                 /* Setup string column */
     117          50 :                 sprintf((char *) strCol[i], "Dummy value %d", i * 3);
     118          50 :                 lStrCol[i] = strlen((char *) strCol[i]);
     119             : 
     120             :                 /* Setup date column */
     121          50 :                 dateCol[i].day = (i % 28) + 1;
     122          50 :                 dateCol[i].month = (i * 5) % 12 + 1;
     123          50 :                 dateCol[i].year = i + 2000;
     124          50 :                 lDateCol[i] = sizeof(SQL_DATE_STRUCT);
     125             : 
     126             :                 /* Setup numeric values column */
     127          50 :                 numericCol[i].precision = 10;
     128          50 :                 numericCol[i].scale = 0;
     129          50 :                 numericCol[i].sign = i % 2;
     130          50 :                 memset(numericCol[i].val, 0, SQL_MAX_NUMERIC_LEN);
     131          50 :                 sprintf((char *) numericCol[i].val, "%x", i * 10);
     132          50 :                 lNumericCol[i] = sizeof(SQL_NUMERIC_STRUCT);
     133             : 
     134             :                 /* Setup binary values column */
     135          50 :                 sprintf((char *) binCol[i], "%d", i * 11);
     136          50 :                 lBinCol[i] = strlen((char *) binCol[i]);
     137             :         }
     138          10 : }
     139             : 
     140             : static void
     141             : dirty_name(SQLWCHAR *name)
     142             : {
     143          16 :         const SQLWCHAR f = name[0];
     144          16 :         name[0] = (f == 'X' || f == 'x') ? 'Y' : 'X';
     145             : }
     146             : 
     147             : /*
     148             :  * Test calling a RPC with a TVP containing 3 columns and 4 rows
     149             :  */
     150             : static void
     151           4 : TestTVPInsert(void)
     152             : {
     153             :         SQLWCHAR *tableName;
     154             :         SQLLEN numRows;
     155             :         SQLHDESC apd;
     156           4 :         bool failed = false;
     157             :         SQLPOINTER ptr;
     158             : 
     159             :         /* here we append some dummy string to check binding with a length */
     160           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType_and_garbage");
     161             : 
     162           4 :         odbc_command("IF OBJECT_ID('TestTVPProc') IS NOT NULL DROP PROC TestTVPProc");
     163           4 :         odbc_command("IF TYPE_ID('TVPType') IS NOT NULL DROP TYPE TVPType");
     164           4 :         odbc_command("IF OBJECT_ID('TVPTable') IS NOT NULL DROP TABLE TVPTable");
     165             : 
     166           4 :         odbc_command("CREATE TABLE TVPTable (PersonID INT PRIMARY KEY, Name VARCHAR(50))");
     167           4 :         odbc_command("CREATE TYPE TVPType " "AS TABLE (vPersonID INT PRIMARY KEY, vName VARCHAR(50))");
     168           4 :         odbc_command("CREATE PROCEDURE TestTVPProc (@TVPParam TVPType READONLY) "
     169             :                 "AS INSERT INTO TVPTable SELECT * FROM @TVPParam");
     170             : 
     171           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     172           4 :         ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
     173           4 :         CHECK_COND((ptr == intCol, "SQL_DESC_DATA_PTR expected %p got %p", intCol, ptr));
     174           4 :         CHKGetStmtAttr(SQL_ATTR_APP_PARAM_DESC, &apd, sizeof(apd), NULL, "S");
     175           4 :         CHKSetDescField(apd, 1, SQL_DESC_CONCISE_TYPE, TDS_INT2PTR(SQL_C_DOUBLE), sizeof(SQLSMALLINT), "S");
     176           4 :         ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
     177           4 :         CHECK_COND((ptr == NULL, "SQL_DESC_DATA_PTR expected %p got %p", NULL, ptr));
     178           4 :         assert(!failed);
     179             : 
     180           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, 7 * sizeof(SQLWCHAR), NULL, "S");
     181           4 :         dirty_name(tableName);
     182             : 
     183           4 :         CHKGetStmtAttr(SQL_ATTR_APP_PARAM_DESC, &apd, sizeof(apd), NULL, "S");
     184           4 :         CHKSetDescField(apd, 1, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER) &numRows, sizeof(void*), "S");
     185             : 
     186           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     187             : 
     188           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     189           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
     190             : 
     191           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     192             : 
     193             :         /* Population of the StrLen_or_IndPtr buffer can be deferred */
     194             :         /* We use one rows less than the maximum to check if code is using the right value */
     195           4 :         numRows = MAX_ROWS - 1;
     196             : 
     197           4 :         CHKExecDirect(T("{CALL TestTVPProc(?)}"), SQL_NTS, "S");
     198             : 
     199             :         /* Ensure that we successfully add 5 rows */
     200           4 :         odbc_command("SELECT COUNT(*) FROM TVPTable");
     201             : 
     202           4 :         CHKFetch("SI");
     203             : 
     204           4 :         CHKGetData(1, SQL_C_CHAR, outputBuffer, sizeof(outputBuffer), &lenBuffer, "S");
     205           4 :         if (atoi((char *) outputBuffer) != numRows) {
     206           0 :                 fprintf(stderr, "Wrong number of rows inserted, expected %ld, got %s\n", (long) numRows, outputBuffer);
     207           0 :                 exit(1);
     208             :         }
     209             : 
     210           4 :         CHKFetch("No");
     211           4 :         CHKCloseCursor("SI");
     212             : 
     213             :         /* check all rows are present */
     214           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 0 AND Name = 'Dummy value 0') SELECT 1");
     215           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 10 AND Name = 'Dummy value 3') SELECT 1");
     216           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 20 AND Name = 'Dummy value 6') SELECT 1");
     217           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 30 AND Name = 'Dummy value 9') SELECT 1");
     218           4 :         odbc_check_no_row("IF EXISTS(SELECT * FROM TVPTable WHERE PersonID = 40 AND Name = 'Dummy value 12') SELECT 1");
     219             : 
     220           4 :         odbc_command("DROP PROC TestTVPProc");
     221           4 :         odbc_command("DROP TYPE TVPType");
     222           4 :         odbc_command("DROP TABLE TVPTable");
     223             : 
     224           4 :         CHKFreeStmt(SQL_RESET_PARAMS, "S");
     225           4 : }
     226             : 
     227             : /*
     228             :  * Test TVP usage with more parameter types, using a TVP of 2 columns and 5 rows
     229             :  */
     230             : static void
     231           4 : TestTVPInsert2(void)
     232             : {
     233             :         SQLWCHAR *tableName;    /* Test explicit schema declaration */
     234             :         SQLLEN numRows;
     235             : 
     236           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType2");
     237             : 
     238           4 :         odbc_command("IF OBJECT_ID('TestTVPProc2') IS NOT NULL DROP PROC TestTVPProc2");
     239           4 :         odbc_command("IF TYPE_ID('TVPType2') IS NOT NULL DROP TYPE TVPType2");
     240           4 :         odbc_command("IF OBJECT_ID('TVPTable2') IS NOT NULL DROP TABLE TVPTable2");
     241             : 
     242           4 :         odbc_command("CREATE TABLE TVPTable2 (Num NUMERIC(10, 5), Bin BINARY(10))");
     243           4 :         odbc_command("CREATE TYPE TVPType2 " "AS TABLE (vNum NUMERIC(10, 5), vBin VARBINARY(10))");
     244           4 :         odbc_command("CREATE PROCEDURE TestTVPProc2 (@TVPParam TVPType2 READONLY) "
     245             :                 "AS INSERT INTO TVPTable2 SELECT vNum, vBin FROM @TVPParam");
     246             : 
     247           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     248           4 :         dirty_name(tableName);
     249             : 
     250           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     251             : 
     252           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
     253             :                 10, 5, numericCol, sizeof(SQL_NUMERIC_STRUCT), lNumericCol, "S");
     254           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
     255             :                 MAX_STRING_LENGTH, 4, binCol, MAX_STRING_LENGTH, lBinCol, "S");
     256             : 
     257           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     258             : 
     259             :         /* Population of the StrLen_or_IndPtr buffer can be deferred */
     260           4 :         numRows = MAX_ROWS;
     261             : 
     262           4 :         CHKExecDirect(T("{CALL TestTVPProc2(?)}"), SQL_NTS, "S");
     263             : 
     264             :         /* Ensure that we successfully add 5 rows */
     265           4 :         odbc_command("SELECT COUNT(*) FROM TVPTable2");
     266             : 
     267           4 :         CHKFetch("SI");
     268             : 
     269           4 :         CHKGetData(1, SQL_C_CHAR, outputBuffer, sizeof(outputBuffer), &lenBuffer, "S");
     270           4 :         if (strcmp((char *) outputBuffer, "5") != 0) {
     271           0 :                 fprintf(stderr, "Wrong number of columns inserted, expected %ld, got %s\n", (long) numRows, outputBuffer);
     272           0 :                 exit(1);
     273             :         }
     274             : 
     275           4 :         CHKFetch("No");
     276           4 :         CHKFetch("No");
     277           4 :         CHKCloseCursor("SI");
     278             : 
     279           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x30 AND Num = -48) SELECT 1");
     280           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3131 AND Num = 97) SELECT 1");
     281           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3232 AND Num = -13361) SELECT 1");
     282           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3333 AND Num = 25905) SELECT 1");
     283           4 :         odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3434 AND Num = -14386) SELECT 1");
     284             : 
     285           4 :         odbc_command("DROP PROC TestTVPProc2");
     286           4 :         odbc_command("DROP TYPE TVPType2");
     287           4 :         odbc_command("DROP TABLE TVPTable2");
     288             : 
     289           4 :         CHKFreeStmt(SQL_RESET_PARAMS, "S");
     290           4 : }
     291             : 
     292             : /*
     293             :  * Test freeing a TVP without executing it - simulates the case
     294             :  * where we encounter an error before calling SQLExecDirect
     295             :  */
     296             : static void
     297           4 : TestTVPMemoryManagement(void)
     298             : {
     299             :         SQLWCHAR *tableName;
     300             :         SQLLEN numRows;
     301             : 
     302           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
     303             : 
     304           4 :         odbc_command("IF OBJECT_ID('TestTVPProc') IS NOT NULL DROP PROC TestTVPProc");
     305           4 :         odbc_command("IF TYPE_ID('TVPType') IS NOT NULL DROP TYPE TVPType");
     306           4 :         odbc_command("IF OBJECT_ID('TVPTable') IS NOT NULL DROP TABLE TVPTable");
     307             : 
     308           4 :         odbc_command("CREATE TABLE TVPTable (PersonID INT PRIMARY KEY, Name VARCHAR(50))");
     309           4 :         odbc_command("CREATE TYPE TVPType " "AS TABLE (vPersonID INT PRIMARY KEY, vName VARCHAR(50))");
     310           4 :         odbc_command("CREATE PROCEDURE TestTVPProc (@TVPParam TVPType READONLY) "
     311             :                 "AS INSERT INTO TVPTable SELECT * FROM @TVPParam");
     312             : 
     313           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     314           4 :         dirty_name(tableName);
     315             : 
     316           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     317             : 
     318           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     319           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
     320             : 
     321           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     322             : 
     323           4 :         odbc_command("DROP PROC TestTVPProc");
     324           4 :         odbc_command("DROP TYPE TVPType");
     325           4 :         odbc_command("DROP TABLE TVPTable");
     326             : 
     327           4 :         CHKFreeStmt(SQL_RESET_PARAMS, "S");
     328           4 : }
     329             : 
     330             : /* Test some errors happens when we expect them */
     331             : static void
     332           4 : TestErrors(void)
     333             : {
     334             :         SQLWCHAR *tableName;
     335             :         SQLLEN numRows;
     336             : 
     337           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
     338             : 
     339             :         /* SQL error 07006 -- [Microsoft][ODBC Driver 17 for SQL Server]Restricted data type attribute violation */
     340           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     341           4 :         odbc_read_error();
     342           4 :         assert(strcmp(odbc_sqlstate, "07006") == 0);
     343             : 
     344             :         /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
     345           4 :         CHKBindParameter(1, SQL_PARAM_OUTPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     346           4 :         odbc_read_error();
     347           4 :         assert(strcmp(odbc_sqlstate, "HY105") == 0);
     348             : 
     349             :         /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
     350           4 :         CHKBindParameter(1, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     351           4 :         odbc_read_error();
     352           4 :         assert(strcmp(odbc_sqlstate, "HY105") == 0);
     353             : 
     354             :         /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
     355           4 :         CHKBindParameter(1, SQL_PARAM_INPUT_OUTPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     356           4 :         odbc_read_error();
     357           4 :         assert(strcmp(odbc_sqlstate, "HY105") == 0);
     358             : 
     359           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     360           4 :         tableName[0] = 'A';
     361             : 
     362           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     363             : 
     364             :         /* SQL error IM020 -- [Microsoft][ODBC Driver 17 for SQL Server]Parameter focus does not refer to a table-valued parameter */
     365           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 2, SQL_IS_INTEGER, "E");
     366           4 :         odbc_read_error();
     367           4 :         assert(strcmp(odbc_sqlstate, "IM020") == 0);
     368             : 
     369           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     370             : 
     371             :         /* SQL error HY004 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid SQL data type */
     372           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     373           4 :         odbc_read_error();
     374           4 :         assert(strcmp(odbc_sqlstate, "HY004") == 0);
     375             : 
     376           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     377           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
     378             : 
     379           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     380             : 
     381           4 :         odbc_reset_statement();
     382           4 : }
     383             : 
     384             : static void
     385           4 : TestDescriptorValues(void)
     386             : {
     387             :         SQLWCHAR *tableName;
     388             :         SQLLEN numRows;
     389             :         SQLPOINTER ptr;
     390             :         SQLSMALLINT count, type;
     391             :         SQLLEN len;
     392           4 :         bool failed = false;
     393             : 
     394           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
     395             : 
     396           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     397           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     398           4 :         count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     399           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     400             : 
     401           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     402           4 :         dirty_name(tableName);
     403             : 
     404           4 :         count = GET_DESC_FIELD(APP, 1, SQL_DESC_LENGTH, SQLULEN);
     405           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     406           4 :         count = GET_DESC_FIELD(IMP, 1, SQL_DESC_LENGTH, SQLULEN);
     407           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     408             : 
     409           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_ARRAY_SIZE, SQLULEN);
     410           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     411             : 
     412           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     413           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     414           4 :         count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     415           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     416             : 
     417             :         /* data pointer should point to table name */
     418           4 :         ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
     419           4 :         CHECK_COND((ptr == tableName, "SQL_DESC_DATA_PTR expected %p got %p", tableName, ptr));
     420           4 :         if (odbc_driver_is_freetds()) {
     421             :                 /* MS driver cannot read this, used internally by FreeTDS */
     422           4 :                 ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
     423           4 :                 printf("Internal pointer %p\n", ptr);
     424             :         }
     425             : 
     426             :         /* indicator should be the pointer to number of rows */
     427           4 :         ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_INDICATOR_PTR, SQLPOINTER);
     428           4 :         CHECK_COND((ptr == &numRows, "SQL_DESC_INDICATOR_PTR expected %p got %p", &numRows, ptr));
     429           4 :         if (odbc_driver_is_freetds()) {
     430           4 :                 ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_INDICATOR_PTR, SQLPOINTER);
     431           4 :                 CHECK_COND((ptr == NULL, "SQL_DESC_INDICATOR_PTR expected %p got %p", NULL, ptr));
     432             :         }
     433             : 
     434             :         /* octect length pointer should be the pointer to number of rows */
     435           4 :         ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_OCTET_LENGTH_PTR, SQLPOINTER);
     436           4 :         CHECK_COND((ptr == &numRows, "SQL_DESC_OCTET_LENGTH_PTR expected %p got %p", &numRows, ptr));
     437           4 :         if (odbc_driver_is_freetds()) {
     438           4 :                 ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_OCTET_LENGTH_PTR, SQLPOINTER);
     439           4 :                 CHECK_COND((ptr == NULL, "SQL_DESC_OCTET_LENGTH_PTR expected %p got %p", NULL, ptr));
     440             :         }
     441             : 
     442             :         /* this should be then length of tableName passed to SQLBindParameter */
     443           4 :         len = GET_DESC_FIELD(APP, 1, SQL_DESC_OCTET_LENGTH, SQLLEN);
     444           4 :         CHECK_COND((len == SQL_NTS, "SQL_DESC_OCTET_LENGTH expected %ld got %ld", (long) SQL_NTS, (long) len));
     445           4 :         len = GET_DESC_FIELD(IMP, 1, SQL_DESC_OCTET_LENGTH, SQLLEN);
     446           4 :         CHECK_COND((len == 0, "SQL_DESC_OCTET_LENGTH expected %ld got %ld", (long) 0, (long) len));
     447             : 
     448           4 :         type = GET_DESC_FIELD(APP, 1, SQL_DESC_CONCISE_TYPE, SQLSMALLINT);
     449           4 :         CHECK_COND((type == SQL_C_BINARY, "SQL_DESC_CONCISE_TYPE expected %d got %d", SQL_C_BINARY, type));
     450           4 :         type = GET_DESC_FIELD(IMP, 1, SQL_DESC_CONCISE_TYPE, SQLSMALLINT);
     451           4 :         CHECK_COND((type == SQL_SS_TABLE, "SQL_DESC_CONCISE_TYPE expected %d got %d", SQL_SS_TABLE, type));
     452             : 
     453             :         /* setting parameter focus should move to different descriptors */
     454           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     455             : 
     456           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     457           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     458           4 :         count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     459           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     460             : 
     461           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_ARRAY_SIZE, SQLULEN);
     462           4 :         CHECK_COND((count == 5, "count %d == 5", (int) count));
     463             : 
     464             :         /* modify descriptors */
     465           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     466           4 :         CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
     467             : 
     468           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     469           4 :         CHECK_COND((count == 2, "count %d == 2", (int) count));
     470           4 :         count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     471           4 :         CHECK_COND((count == 2, "count %d == 2", (int) count));
     472             : 
     473             :         /* switch back to main descriptors */
     474           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     475             : 
     476           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     477           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     478           4 :         count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     479           4 :         CHECK_COND((count == 1, "count %d == 1", (int) count));
     480             : 
     481             :         /* switch back to table */
     482           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     483             : 
     484             :         /* this should fail, cannot set table inside a table */
     485           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
     486             : 
     487           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
     488             : 
     489             :         /* reset parameters, we should reset TVP */
     490           4 :         CHKFreeStmt(SQL_RESET_PARAMS, "S");
     491             : 
     492           4 :         count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
     493           4 :         CHECK_COND((count == 0, "count %d == 0", (int) count));
     494             : 
     495           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     496             : 
     497           4 :         assert(!failed);
     498             : 
     499           4 :         odbc_reset_statement();
     500           4 : }
     501             : 
     502             : #ifdef MEMORY_TESTS
     503             : static size_t
     504          16 : memory_usage(void)
     505             : {
     506          16 :         size_t ret = 0;
     507             : 
     508             :         /* mallinfo does not work on Valgrind, ignore */
     509          16 :         if (RUNNING_ON_VALGRIND > 0)
     510             :                 return ret;
     511             : 
     512             : #if defined(HAVE__HEAPWALK)
     513             :         {
     514             :                 _HEAPINFO hinfo;
     515             :                 int heapstatus;
     516             : 
     517             :                 hinfo._pentry = NULL;
     518             :                 while ((heapstatus = _heapwalk(&hinfo)) == _HEAPOK) {
     519             :                         if (hinfo._useflag == _USEDENTRY)
     520             :                                 ret += hinfo._size;
     521             :                 }
     522             :                 assert(heapstatus == _HEAPEMPTY || heapstatus == _HEAPEND);
     523             :         }
     524             : #elif defined(HAVE_MALLINFO2)
     525             :         ret = mallinfo2().uordblks;
     526             : #else
     527           0 :         ret = (size_t) (mallinfo().uordblks);
     528             : #endif
     529           0 :         return ret;
     530             : }
     531             : 
     532             : static void
     533           4 : TestInitializeLeak(void)
     534             : {
     535             :         SQLWCHAR *tableName;
     536             :         SQLLEN numRows;
     537             :         size_t initial_memory;
     538             :         int i;
     539             : 
     540           4 :         tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
     541             : 
     542           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     543             : 
     544           4 :         CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
     545             : 
     546           4 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     547             : 
     548             :         /* try to repeat binding column */
     549           4 :         initial_memory = memory_usage();
     550        4100 :         for (i = 0; i < 1024; ++i)
     551        4096 :                 CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
     552             : 
     553             :         /* memory should not increase a lot */
     554           4 :         assert(memory_usage() < initial_memory + 10240);
     555             : 
     556           4 :         odbc_reset_statement();
     557             : 
     558             :         /* check we don't leak binding table multiple times */
     559             :         /* this leak memory on MS driver */
     560           4 :         if (odbc_driver_is_freetds()) {
     561           4 :                 CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     562             : 
     563             :                 /* try to repeat set of the table */
     564           4 :                 initial_memory = memory_usage();
     565        4100 :                 for (i = 0; i < 1024; ++i)
     566        4096 :                         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
     567             : 
     568             :                 /* memory should not increase a lot */
     569           4 :                 assert(memory_usage() < initial_memory + 10240);
     570             : 
     571           4 :                 odbc_reset_statement();
     572             :         }
     573           4 : }
     574             : #endif
     575             : 
     576             : int
     577          10 : main(void)
     578             : {
     579          10 :         odbc_use_version3 = 1;
     580             : 
     581          10 :         setup();
     582             : 
     583          10 :         odbc_connect();
     584             : 
     585          10 :         if (odbc_tds_version() < 0x703) {
     586           6 :                 odbc_disconnect();
     587           6 :                 printf("TVP data is supported since protocol 7.3, MSSQL only.\n");
     588           6 :                 odbc_test_skipped();
     589           0 :                 return 0;
     590             :         }
     591             : 
     592           4 :         TestTVPInsert();
     593           4 :         TestTVPInsert2();
     594           4 :         TestTVPMemoryManagement();
     595           4 :         TestErrors();
     596           4 :         TestDescriptorValues();
     597             : 
     598             : #ifdef MEMORY_TESTS
     599           4 :         TestInitializeLeak();
     600             : #endif
     601             : 
     602           4 :         odbc_disconnect();
     603             : 
     604           4 :         return 0;
     605             : }

Generated by: LCOV version 1.13