LCOV - code coverage report
Current view: top level - src/odbc/unittests - describeparam.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 153 163 93.9 %
Date: 2026-07-19 14:02:14 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /* Test SQLDescribeParam */
       2             : 
       3             : #include "common.h"
       4             : 
       5             : static void
       6          82 : check_int(bool cond, long int value, const char *msg, int line)
       7             : {
       8          82 :         if (cond)
       9          82 :                 return;
      10           0 :         fprintf(stderr, "Invalid value %ld at line %d, check: %s\n", value, line, msg);
      11           0 :         exit(1);
      12             : }
      13             : 
      14             : #define check_int(value, cond, expected) \
      15             :         check_int(value cond expected, value, #value " " #cond " " #expected, __LINE__)
      16             : 
      17             : static void
      18          32 : check_type(bool cond, SQLSMALLINT value, const char *msg, int line)
      19             : {
      20          32 :         if (cond)
      21          32 :                 return;
      22           0 :         fprintf(stderr, "Invalid value %d(%s) at line %d, check: %s\n",
      23             :                 value, odbc_lookup_value(value, odbc_sql_types, "???"), line, msg);
      24           0 :         exit(1);
      25             : }
      26             : 
      27             : #define check_type(value, cond, expected) \
      28             :         check_type(value cond expected, value, #value " " #cond " " #expected, __LINE__)
      29             : 
      30          10 : TEST_MAIN()
      31             : {
      32             :         SQLSMALLINT num_params;
      33             :         SQLSMALLINT sql_type;
      34             :         SQLULEN size;
      35             :         SQLSMALLINT digits, scale, nullable, count;
      36             :         SQLHDESC ipd, apd;
      37             :         SQLINTEGER ind;
      38          10 :         SQLLEN sql_nts = SQL_NTS;
      39             :         SQLINTEGER id;
      40             :         const char *env;
      41             : 
      42          10 :         odbc_connect();
      43             : 
      44          10 :         if (!odbc_db_is_microsoft() || odbc_db_version_int() < 0x0b000000u) {
      45           8 :                 odbc_disconnect();
      46           8 :                 printf("SQLDescribeParam implementation requires MSSQL 2012+\n");
      47           8 :                 odbc_test_skipped();
      48           0 :                 return 0;
      49             :         }
      50             : 
      51           2 :         odbc_command("IF OBJECT_ID('describe') IS NOT NULL DROP TABLE describe");
      52           2 :         odbc_command("CREATE TABLE describe(i int NOT NULL, vc VARCHAR(100) NULL, "
      53             :                      "vb VARBINARY(100) NULL, num NUMERIC(17,5) NULL)");
      54           2 :         odbc_reset_statement();
      55             : 
      56           2 :         CHKPrepare(T("INSERT INTO describe(i, vc, num) VALUES(?, ?, ?)"), SQL_NTS, "S");
      57             : 
      58             :         /* we prepared a query with 3 parameters, we should have 3 parameters */
      59           2 :         CHKNumParams(&num_params, "S");
      60           2 :         check_int(num_params, ==, 3);
      61             : 
      62           2 :         CHKGetStmtAttr(SQL_ATTR_IMP_PARAM_DESC, &ipd, sizeof(ipd), &ind, "S");
      63           2 :         CHKGetStmtAttr(SQL_ATTR_APP_PARAM_DESC, &apd, sizeof(apd), &ind, "S");
      64             : 
      65             :         /* check we have no parameters on IPD and APD */
      66           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
      67           2 :         check_int(count, ==, 0);
      68           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
      69           2 :         check_int(count, ==, 0);
      70             : 
      71             :         /* get some parameters */
      72           2 :         CHKDescribeParam(0, &sql_type, &size, &digits, &nullable, "E");
      73           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
      74           2 :         check_type(sql_type, ==, SQL_INTEGER);
      75           2 :         check_int(size, ==, 10);
      76           2 :         check_int(digits, ==, 0);
      77           2 :         check_int(nullable, ==, SQL_NULLABLE);
      78           2 :         CHKGetDescField(ipd, 1, SQL_DESC_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
      79           2 :         check_type(sql_type, ==, SQL_INTEGER);
      80           2 :         CHKGetDescField(ipd, 1, SQL_DESC_CONCISE_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
      81           2 :         check_type(sql_type, ==, SQL_INTEGER);
      82           2 :         CHKGetDescField(ipd, 1, SQL_DESC_LENGTH, &size, sizeof(SQLULEN), &ind, "S");
      83           2 :         check_int(size, ==, 10);
      84           2 :         CHKGetDescField(ipd, 1, SQL_DESC_PRECISION, &digits, sizeof(SQLSMALLINT), &ind, "S");
      85             :         /* TODO sligthly difference with MS driver about descriptor handling */
      86           2 :         if (!odbc_driver_is_freetds())
      87           0 :                 check_int(digits, ==, 10);
      88           2 :         CHKGetDescField(ipd, 1, SQL_DESC_SCALE, &scale, sizeof(SQLSMALLINT), &ind, "S");
      89           2 :         check_int(scale, ==, 0);
      90             : 
      91           2 :         CHKDescribeParam(2, &sql_type, &size, &digits, &nullable, "S");
      92           2 :         check_type(sql_type, ==, SQL_VARCHAR);
      93           2 :         check_int(size, ==, 100);
      94           2 :         check_int(digits, ==, 0);
      95           2 :         check_int(nullable, ==, SQL_NULLABLE);
      96           2 :         CHKGetDescField(ipd, 2, SQL_DESC_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
      97           2 :         check_type(sql_type, ==, SQL_VARCHAR);
      98           2 :         CHKGetDescField(ipd, 2, SQL_DESC_CONCISE_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
      99           2 :         check_type(sql_type, ==, SQL_VARCHAR);
     100           2 :         CHKGetDescField(ipd, 2, SQL_DESC_LENGTH, &size, sizeof(SQLULEN), &ind, "S");
     101           2 :         check_int(size, ==, 100);
     102           2 :         CHKGetDescField(ipd, 2, SQL_DESC_PRECISION, &digits, sizeof(SQLSMALLINT), &ind, "S");
     103           2 :         if (!odbc_driver_is_freetds())
     104           0 :                 check_int(digits, ==, 100);
     105           2 :         CHKGetDescField(ipd, 2, SQL_DESC_SCALE, &scale, sizeof(SQLSMALLINT), &ind, "S");
     106           2 :         check_int(scale, ==, 0);
     107             : 
     108           2 :         CHKDescribeParam(3, &sql_type, &size, &digits, &nullable, "S");
     109           2 :         check_type(sql_type, ==, SQL_NUMERIC);
     110           2 :         check_int(size, ==, 17);
     111           2 :         check_int(digits, ==, 5);
     112           2 :         check_int(nullable, ==, SQL_NULLABLE);
     113           2 :         CHKGetDescField(ipd, 3, SQL_DESC_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
     114           2 :         check_type(sql_type, ==, SQL_NUMERIC);
     115           2 :         CHKGetDescField(ipd, 3, SQL_DESC_CONCISE_TYPE, &sql_type, sizeof(SQLSMALLINT), &ind, "S");
     116           2 :         check_type(sql_type, ==, SQL_NUMERIC);
     117           2 :         CHKGetDescField(ipd, 3, SQL_DESC_LENGTH, &size, sizeof(SQLULEN), &ind, "S");
     118           2 :         check_int(size, ==, 17);
     119           2 :         CHKGetDescField(ipd, 3, SQL_DESC_PRECISION, &digits, sizeof(SQLSMALLINT), &ind, "S");
     120           2 :         check_int(digits, ==, 17);
     121           2 :         CHKGetDescField(ipd, 3, SQL_DESC_SCALE, &scale, sizeof(SQLSMALLINT), &ind, "S");
     122           2 :         check_int(scale, ==, 5);
     123             : 
     124           2 :         CHKDescribeParam(4, &sql_type, &size, &digits, &nullable, "E");
     125             : 
     126             :         /* check parameters were filled on IPD */
     127           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     128           2 :         check_int(count, ==, 3);
     129             :         /* APD is not filled */
     130           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     131           2 :         check_int(count, ==, 0);
     132             : 
     133             :         /*****************************************************************/
     134             : 
     135             :         /* preparing another query  */
     136           2 :         CHKPrepare(T("INSERT INTO describe(i) VALUES(?)"), SQL_NTS, "S");
     137             : 
     138             :         /* we prepared a query with 1 parameter, we should have 1 parameter */
     139           2 :         CHKNumParams(&num_params, "S");
     140           2 :         check_int(num_params, ==, 1);
     141             : 
     142           2 :         CHKPrepare(T("INSERT INTO describe(i, vc) VALUES(?, ?)"), SQL_NTS, "S");
     143             : 
     144             :         /* check we have no parameters on IPD and APD */
     145             :         /* SQLPrepare should clear them */
     146           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     147           2 :         check_int(count, ==, 0);
     148           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     149           2 :         check_int(count, ==, 0);
     150             : 
     151           2 :         CHKNumParams(&num_params, "S");
     152             : 
     153             :         /* check we have no parameters on IPD and APD */
     154             :         /* SQLNumParams does not affect them */
     155           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     156           2 :         check_int(count, ==, 0);
     157           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     158           2 :         check_int(count, ==, 0);
     159             : 
     160           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
     161           2 :         check_type(sql_type, ==, SQL_INTEGER);
     162             : 
     163             :         /* check parameters were filled on IPD */
     164           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     165           2 :         check_int(count, ==, 2);
     166             :         /* APD is not filled */
     167           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     168           2 :         check_int(count, ==, 0);
     169             : 
     170             :         /*****************************************************************/
     171             : 
     172             :         /* bind a parameter, see if affects SQLDescribeParam */
     173           2 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_FLOAT, 10, 0, &id, 0, &sql_nts, "S");
     174           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     175           2 :         check_int(count, ==, 1);
     176             : 
     177             :         /* what happens preparing another query ? */
     178           2 :         CHKPrepare(T("INSERT INTO describe(vc, i, vb) VALUES(?, ?, ?)"), SQL_NTS, "S");
     179             : 
     180             :         /* type do not change, set one stays */
     181           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
     182           2 :         check_type(sql_type, ==, SQL_FLOAT);
     183             :         /* even this parameter remains */
     184           2 :         CHKDescribeParam(2, &sql_type, &size, &digits, &nullable, "S");
     185           2 :         check_type(sql_type, ==, SQL_VARCHAR);
     186             :         /* additional parameter are not read from server */
     187             :         /* "SQL error 07009 -- [Microsoft][ODBC Driver 18 for SQL Server]Invalid Descriptor Index" */
     188           2 :         CHKDescribeParam(3, &sql_type, &size, &digits, &nullable, "E");
     189             : 
     190           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     191           2 :         check_int(count, ==, 2);
     192             :         /* APD remains */
     193           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     194           2 :         check_int(count, ==, 1);
     195             : 
     196             :         /*****************************************************************/
     197             : 
     198             :         /* try to reset APD */
     199           2 :         CHKSetDescField(apd, 1, SQL_DESC_COUNT, (SQLPOINTER) 0, SQL_IS_SMALLINT, "S");
     200             : 
     201           2 :         CHKPrepare(T("INSERT INTO describe(i) VALUES(?)"), SQL_NTS, "S");
     202             : 
     203             :         /* parameter is not overridden */
     204           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
     205           2 :         check_type(sql_type, ==, SQL_FLOAT);
     206             : 
     207           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     208           2 :         check_int(count, ==, 2);
     209           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     210           2 :         check_int(count, ==, 0);
     211             : 
     212             :         /*****************************************************************/
     213             : 
     214             :         /* try to reset parameters */
     215           2 :         CHKFreeStmt(SQL_RESET_PARAMS, "S");
     216             : 
     217           2 :         CHKPrepare(T("INSERT INTO describe(i) VALUES(?)"), SQL_NTS, "S");
     218             : 
     219             :         /* parameter is overridden */
     220           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
     221           2 :         check_type(sql_type, ==, SQL_INTEGER);
     222             : 
     223           2 :         CHKGetDescField(ipd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     224           2 :         check_int(count, ==, 1);
     225           2 :         CHKGetDescField(apd, 0, SQL_DESC_COUNT, &count, sizeof(count), &ind, "S");
     226           2 :         check_int(count, ==, 0);
     227             : 
     228             :         /*****************************************************************/
     229             : 
     230             :         /* check what happens if connection is busy (no MARS, pending data) */
     231           2 :         env = getenv("ODBC_MARS");
     232           3 :         if (!env || atoi(env) == 0) {
     233             :                 SQLHSTMT stmt;
     234             : 
     235           1 :                 CHKAllocStmt(&stmt, "S");
     236             : 
     237           1 :                 SWAP_STMT(stmt);
     238           1 :                 CHKExecDirect(T("SELECT * FROM sysobjects(NOLOCK)"), SQL_NTS, "S");
     239             : 
     240           1 :                 SWAP_STMT(stmt);
     241           1 :                 CHKPrepare(T("INSERT INTO describe(vc) VALUES(?)"), SQL_NTS, "S");
     242             : 
     243           1 :                 CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "E");
     244           1 :                 odbc_read_error();
     245           1 :                 if (strcmp(odbc_sqlstate, "07009") != 0) {
     246           0 :                         fprintf(stderr, "Unexpected sql state returned: %s\n", odbc_sqlstate);
     247           0 :                         odbc_disconnect();
     248           0 :                         exit(1);
     249             :                 }
     250             : 
     251           1 :                 SWAP_STMT(stmt);
     252           1 :                 CHKMoreResults("SNo");
     253           1 :                 CHKMoreResults("No");
     254           1 :                 CHKFreeStmt(SQL_DROP, "S");
     255             : 
     256           1 :                 SWAP_STMT(stmt);
     257             :         }
     258             : 
     259             :         /*****************************************************************/
     260             : 
     261             :         /* prepare a stored procedure */
     262           2 :         CHKPrepare(T("{?=call sp_tables(?)}"), SQL_NTS, "S");
     263             : 
     264           2 :         CHKDescribeParam(1, &sql_type, &size, &digits, &nullable, "S");
     265           2 :         check_type(sql_type, ==, SQL_INTEGER);
     266           2 :         check_int(size, ==, 10);
     267           2 :         check_int(digits, ==, 0);
     268           2 :         check_int(nullable, ==, SQL_NULLABLE);
     269           2 :         CHKDescribeParam(2, &sql_type, &size, &digits, &nullable, "S");
     270           2 :         check_type(sql_type, ==, SQL_WVARCHAR);
     271           2 :         check_int(size, ==, 384);
     272           2 :         check_int(digits, ==, 0);
     273           2 :         check_int(nullable, ==, SQL_NULLABLE);
     274             : 
     275             :         /*****************************************************************/
     276             : 
     277             :         /* cleanup */
     278           2 :         odbc_command("DROP TABLE describe");
     279             : 
     280           2 :         odbc_disconnect();
     281           2 :         return 0;
     282             : }

Generated by: LCOV version 1.13