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

Generated by: LCOV version 1.13