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

Generated by: LCOV version 1.13