LCOV - code coverage report
Current view: top level - src/odbc/unittests - testodbc.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 144 156 92.3 %
Date: 2025-01-18 11:50:39 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Code to test ODBC implementation.
       3             :  *  - David Fraser, Abelon Systems 2003.
       4             :  */
       5             : 
       6             : #include "common.h"
       7             : 
       8             : #ifdef DEBUG
       9             : # define AB_FUNCT(x)  do { printf x; printf("\n"); } while(0)
      10             : # define AB_PRINT(x)  do { printf x; printf("\n"); } while(0)
      11             : #else
      12             : # define AB_FUNCT(x)
      13             : # define AB_PRINT(x)
      14             : #endif
      15             : #define AB_ERROR(x)   do { printf("ERROR: "); printf x; printf("\n"); } while(0)
      16             : 
      17             : typedef bool DbTestFn(void);
      18             : 
      19             : static bool RunTests(void);
      20             : 
      21             : typedef struct
      22             : {
      23             :         DbTestFn *testFn;
      24             :         const char *description;
      25             : } DbTestEntry;
      26             : 
      27             : /*
      28             :  * Test that makes a parameterized ODBC query using SQLPrepare and SQLExecute
      29             :  */
      30             : static bool
      31           8 : TestRawODBCPreparedQuery(void)
      32             : {
      33             :         SQLTCHAR *queryString;
      34           8 :         SQLLEN lenOrInd = 0;
      35           8 :         SQLSMALLINT supplierId = 4;
      36             :         int count;
      37             : 
      38           8 :         AB_FUNCT(("TestRawODBCPreparedQuery (in)"));
      39             : 
      40             :         /* INIT */
      41             : 
      42           8 :         odbc_connect();
      43             : 
      44             :         /* MAKE QUERY */
      45             : 
      46           8 :         odbc_command("CREATE TABLE #Products ("
      47             :                 "ProductID int NOT NULL ,"
      48             :                 "ProductName varchar (40) ,"
      49             :                 "SupplierID int NULL ,"
      50             :                 "CategoryID int NULL ,"
      51             :                 "QuantityPerUnit varchar (20)  ,"
      52             :                 "UnitPrice money NULL ,"
      53             :                 "UnitsInStock smallint NULL ,"
      54             :                 "UnitsOnOrder smallint NULL ,"
      55             :                 "ReorderLevel smallint NULL ,"
      56             :                 "Discontinued bit NOT NULL "
      57             :                 ") "
      58             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(9,'Mishi Kobe Niku',4,6,'18 - 500 g pkgs.',97.00,29,0,0,1) "
      59             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(10,'Ikura',4,8,'12 - 200 ml jars',31.00,31,0,0,0) "
      60             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(74,'Longlife Tofu',4,7,'5 kg pkg.',10.00,4,20,5,0) "
      61             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(11,'Queso Cabrales',5,4,'1 kg pkg.',21.00,22,30,30,0) "
      62             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(12,'Queso Manchego La Pastora',5,4,'10 - 500 g pkgs.',38.00,86,0,0,0)");
      63          48 :         while (SQLMoreResults(odbc_stmt) == SQL_SUCCESS)
      64          32 :                 continue;
      65             : 
      66           8 :         queryString = T("SELECT * FROM #Products WHERE SupplierID = ?");
      67             : 
      68           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &supplierId, 0, &lenOrInd, "S");
      69             : 
      70           8 :         CHKPrepare(queryString, SQL_NTS, "S");
      71             : 
      72           8 :         CHKExecute("S");
      73             : 
      74           8 :         count = 0;
      75             : 
      76          40 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
      77          24 :                 count++;
      78             :         }
      79           8 :         AB_PRINT(("Got %d rows", count));
      80             : 
      81           8 :         if (count != 3) {
      82             :                 /*
      83             :                  * OK - so 3 is a magic number - it's the number of rows matching
      84             :                  * this query from the MS sample Northwind database and is a constant.
      85             :                  */
      86           0 :                 AB_ERROR(("Expected %d rows - but got %d rows", 3, count));
      87           0 :                 AB_FUNCT(("TestRawODBCPreparedQuery (out): error"));
      88           0 :                 return false;
      89             :         }
      90             : 
      91             :         /* CLOSEDOWN */
      92             : 
      93           8 :         odbc_disconnect();
      94             : 
      95           8 :         AB_FUNCT(("TestRawODBCPreparedQuery (out): ok"));
      96           8 :         return true;
      97             : }
      98             : 
      99             : /*
     100             :  * Test that makes a parameterized ODBC query using SQLExecDirect.
     101             :  */
     102             : static bool
     103           8 : TestRawODBCDirectQuery(void)
     104             : {
     105           8 :         SQLLEN lenOrInd = 0;
     106           8 :         SQLSMALLINT supplierId = 1;
     107             :         int count;
     108             : 
     109           8 :         AB_FUNCT(("TestRawODBCDirectQuery (in)"));
     110             : 
     111             :         /* INIT */
     112             : 
     113           8 :         odbc_connect();
     114             : 
     115             :         /* MAKE QUERY */
     116             : 
     117           8 :         odbc_command("CREATE TABLE #Products ("
     118             :                 "ProductID int NOT NULL ,"
     119             :                 "ProductName varchar (40) ,"
     120             :                 "SupplierID int NULL ,"
     121             :                 "CategoryID int NULL ,"
     122             :                 "QuantityPerUnit varchar (20)  ,"
     123             :                 "UnitPrice money NULL ,"
     124             :                 "UnitsInStock smallint NULL ,"
     125             :                 "UnitsOnOrder smallint NULL ,"
     126             :                 "ReorderLevel smallint NULL ,"
     127             :                 "Discontinued bit NOT NULL "
     128             :                 ") "
     129             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(1,'Chai',1,1,'10 boxes x 20 bags',18.00,39,0,10,0) "
     130             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(2,'Chang',1,1,'24 - 12 oz bottles',19.00,17,40,25,0) "
     131             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(3,'Aniseed Syrup',1,2,'12 - 550 ml bottles',10.00,13,70,25,0) "
     132             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(4,'Chef Anton''s Cajun Seasoning',2,2,'48 - 6 oz jars',22.00,53,0,0,0) "
     133             :                 "INSERT INTO #Products(ProductID,ProductName,SupplierID,CategoryID,QuantityPerUnit,UnitPrice,UnitsInStock,UnitsOnOrder,ReorderLevel,Discontinued) VALUES(5,'Chef Anton''s Gumbo Mix',2,2,'36 boxes',21.35,0,0,0,1) ");
     134          48 :         while (SQLMoreResults(odbc_stmt) == SQL_SUCCESS)
     135          32 :                 continue;
     136             : 
     137           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &supplierId, 0, &lenOrInd, "S");
     138             : 
     139           8 :         CHKExecDirect(T("SELECT * FROM #Products WHERE SupplierID = ?"), SQL_NTS, "S");
     140             : 
     141           8 :         count = 0;
     142             : 
     143          40 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     144          24 :                 count++;
     145             :         }
     146           8 :         AB_PRINT(("Got %d rows", count));
     147             : 
     148           8 :         if (count != 3) {
     149             :                 /*
     150             :                  * OK - so 3 is a magic number - it's the number of rows matching
     151             :                  * this query from the MS sample Northwind database and is a constant.
     152             :                  */
     153           0 :                 AB_ERROR(("Expected %d rows - but got %d rows", 3, count));
     154           0 :                 AB_FUNCT(("TestRawODBCDirectQuery (out): error"));
     155           0 :                 return false;
     156             :         }
     157             : 
     158             :         /* CLOSEDOWN */
     159             : 
     160           8 :         odbc_disconnect();
     161             : 
     162           8 :         AB_FUNCT(("TestRawODBCDirectQuery (out): ok"));
     163           8 :         return true;
     164             : }
     165             : 
     166             : /*
     167             :  * Test that show what works and what doesn't for the poorly
     168             :  * documented GUID.
     169             :  */
     170             : static bool
     171           8 : TestRawODBCGuid(void)
     172             : {
     173             :         SQLRETURN status;
     174             : 
     175             :         const char *queryString;
     176             :         SQLLEN lenOrInd;
     177             :         SQLSMALLINT age;
     178             :         char guid[40];
     179             :         SQLCHAR name[20];
     180             : 
     181             :         SQLGUID sqlguid;
     182           8 :         int count = 0;
     183             : 
     184           8 :         AB_FUNCT(("TestRawODBCGuid (in)"));
     185             : 
     186           8 :         odbc_connect();
     187             :         
     188           8 :         if (!odbc_db_is_microsoft()) {
     189           2 :                 odbc_disconnect();
     190           2 :                 return true;
     191             :         }
     192             : 
     193           6 :         AB_PRINT(("Creating #pet table"));
     194             : 
     195           6 :         queryString = "CREATE TABLE #pet (name VARCHAR(20), owner VARCHAR(20), "
     196             :                "species VARCHAR(20), sex CHAR(1), age INTEGER, " "guid UNIQUEIDENTIFIER DEFAULT NEWID() ); ";
     197           6 :         CHKExecDirect(T(queryString), SQL_NTS, "SNo");
     198             : 
     199           6 :         odbc_command_with_result(odbc_stmt, "DROP PROC GetGUIDRows");
     200             : 
     201           6 :         AB_PRINT(("Creating stored proc GetGUIDRows"));
     202             : 
     203           6 :         queryString = "CREATE PROCEDURE GetGUIDRows (@guidpar uniqueidentifier) AS \
     204             :                 SELECT name, guid FROM #pet WHERE guid = @guidpar";
     205           6 :         CHKExecDirect(T(queryString), SQL_NTS, "SNo");
     206             : 
     207           6 :         AB_PRINT(("Insert row 1"));
     208             : 
     209           6 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age ) \
     210             :                          VALUES ( 'Fang', 'Mike', 'dog', 'm', 12 );";
     211           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     212             : 
     213           6 :         AB_PRINT(("Insert row 2"));
     214             : 
     215             :         /*
     216             :          * Ok - new row with explicit GUID, but parameterised age.
     217             :          */
     218           6 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     219             :                          VALUES ( 'Splash', 'Dan', 'fish', 'm', ?, \
     220             :                          '12345678-1234-1234-1234-123456789012' );";
     221             : 
     222           6 :         lenOrInd = 0;
     223           6 :         age = 3;
     224           6 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &age, 0, &lenOrInd, "S");
     225             : 
     226           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     227           6 :         CHKFreeStmt(SQL_CLOSE, "S");
     228             : 
     229           6 :         AB_PRINT(("Insert row 3"));
     230             :         /*
     231             :          * Ok - new row with parameterised GUID.
     232             :          */
     233           6 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     234             :                          VALUES ( 'Woof', 'Tom', 'cat', 'f', 2, ? );";
     235             : 
     236           6 :         lenOrInd = SQL_NTS;
     237           6 :         strcpy(guid, "87654321-4321-4321-4321-123456789abc");
     238             : 
     239           6 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_GUID, 0, 0, guid, 0, &lenOrInd, "S");
     240           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     241             : 
     242           6 :         AB_PRINT(("Insert row 4"));
     243             :         /*
     244             :          * Ok - new row with parameterised GUID.
     245             :          */
     246           6 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     247             :                          VALUES ( 'Spike', 'Diane', 'pig', 'f', 4, ? );";
     248             : 
     249           6 :         lenOrInd = SQL_NTS;
     250           6 :         strcpy(guid, "1234abcd-abcd-abcd-abcd-123456789abc");
     251             : 
     252           6 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 36, 0, guid, 0, &lenOrInd, "S");
     253           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     254             : 
     255           6 :         AB_PRINT(("Insert row 5"));
     256             :         /*
     257             :          * Ok - new row with parameterised GUID.
     258             :          */
     259           6 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     260             :                          VALUES ( 'Fluffy', 'Sam', 'dragon', 'm', 16, ? );";
     261             : 
     262           6 :         sqlguid.Data1 = 0xaabbccdd;
     263           6 :         sqlguid.Data2 = 0xeeff;
     264           6 :         sqlguid.Data3 = 0x1122;
     265           6 :         sqlguid.Data4[0] = 0x11;
     266           6 :         sqlguid.Data4[1] = 0x22;
     267           6 :         sqlguid.Data4[2] = 0x33;
     268           6 :         sqlguid.Data4[3] = 0x44;
     269           6 :         sqlguid.Data4[4] = 0x55;
     270           6 :         sqlguid.Data4[5] = 0x66;
     271           6 :         sqlguid.Data4[6] = 0x77;
     272           6 :         sqlguid.Data4[7] = 0x88;
     273             : 
     274           6 :         lenOrInd = 16;
     275           6 :         strcpy(guid, "1234abcd-abcd-abcd-abcd-123456789abc");
     276             : 
     277           6 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID, 16, 0, &sqlguid, 16, &lenOrInd, "S");
     278           6 :         status = SQLExecDirect(odbc_stmt, T(queryString), SQL_NTS);
     279           6 :         if (status != SQL_SUCCESS) {
     280           0 :                 AB_ERROR(("Insert row 5 failed"));
     281           0 :                 AB_ERROR(("Sadly this was expected in *nix ODBC. Carry on."));
     282             :         }
     283             : 
     284             :         /*
     285             :          * Now retrieve rows - especially GUID column values.
     286             :          */
     287           6 :         AB_PRINT(("retrieving name and guid"));
     288           6 :         queryString = "SELECT name, guid FROM #pet";
     289           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     290          42 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     291          30 :                 count++;
     292          30 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     293          30 :                 CHKGetData(2, SQL_CHAR, guid, 37, 0, "S");
     294             : 
     295          30 :                 AB_PRINT(("name: %-10s guid: %s", name, guid));
     296             :         }
     297             : 
     298             :         /*
     299             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     300             :          * state if we try SELECT again).
     301             :          */
     302           6 :         odbc_reset_statement();
     303             : 
     304             : 
     305             :         /*
     306             :          * Now retrieve rows - especially GUID column values.
     307             :          */
     308             : 
     309           6 :         AB_PRINT(("retrieving name and guid again"));
     310           6 :         queryString = "SELECT name, guid FROM #pet";
     311           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     312          42 :         while (CHKFetch("SNo") == SQL_SUCCESS) {
     313          30 :                 count++;
     314          30 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     315          30 :                 CHKGetData(2, SQL_GUID, &sqlguid, 16, 0, "S");
     316             : 
     317          30 :                 AB_PRINT(("%-10s %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
     318             :                           name,
     319             :                           (int) (sqlguid.Data1), sqlguid.Data2,
     320             :                           sqlguid.Data3, sqlguid.Data4[0], sqlguid.Data4[1],
     321             :                           sqlguid.Data4[2], sqlguid.Data4[3], sqlguid.Data4[4],
     322             :                           sqlguid.Data4[5], sqlguid.Data4[6], sqlguid.Data4[7]));
     323             :         }
     324             : 
     325             :         /*
     326             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     327             :          * state if we try SELECT again).
     328             :          */
     329           6 :         odbc_reset_statement();
     330             : 
     331             :         /*
     332             :          * Now retrieve rows via stored procedure passing GUID as param.
     333             :          */
     334           6 :         AB_PRINT(("retrieving name and guid"));
     335             : 
     336           6 :         queryString = "{call GetGUIDRows(?)}";
     337           6 :         lenOrInd = SQL_NTS;
     338           6 :         strcpy(guid, "87654321-4321-4321-4321-123456789abc");
     339             : 
     340           6 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_GUID, 0, 0, guid, 0, &lenOrInd, "S");
     341           6 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     342          18 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     343           6 :                 count++;
     344           6 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     345           6 :                 CHKGetData(2, SQL_CHAR, guid, 37, 0, "S");
     346             : 
     347           6 :                 AB_PRINT(("%-10s %s", name, guid));
     348             :         }
     349             : 
     350             :         /*
     351             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     352             :          * state after a previous SELECT has occurred).
     353             :          */
     354           6 :         odbc_reset_statement();
     355             : 
     356             :         /* cleanup */
     357           6 :         odbc_command_with_result(odbc_stmt, "DROP PROC GetGUIDRows");
     358             : 
     359             :         /* CLOSEDOWN */
     360             : 
     361           6 :         odbc_disconnect();
     362             : 
     363           6 :         AB_FUNCT(("TestRawODBCGuid (out): ok"));
     364           6 :         return true;
     365             : }
     366             : 
     367             : /**
     368             :  * Array of tests.
     369             :  */
     370             : static const DbTestEntry tests[] = {
     371             :         /* 1 */ {TestRawODBCDirectQuery, "Raw ODBC direct query"},
     372             :         /* 2 */ {TestRawODBCPreparedQuery, "Raw ODBC prepared query"},
     373             :         /* 3 */ {TestRawODBCGuid, "Raw ODBC GUID"},
     374             :         /* end */ {NULL, NULL}
     375             : };
     376             : 
     377             : /**
     378             :  * Code to iterate through all tests to run.
     379             :  *
     380             :  * \return
     381             :  *      true if all tests pass, false if any tests fail.
     382             :  */
     383             : static bool
     384           8 : RunTests(void)
     385             : {
     386             :         unsigned int i;
     387           8 :         unsigned int passes = 0;
     388           8 :         unsigned int fails = 0;
     389             : 
     390           8 :         i = 0;
     391          40 :         while (tests[i].testFn) {
     392          24 :                 printf("Running test %2d: %s... ", i + 1, tests[i].description);
     393          24 :                 fflush(stdout);
     394          24 :                 if (tests[i].testFn()) {
     395          24 :                         printf("pass\n");
     396          24 :                         passes++;
     397             :                 } else {
     398           0 :                         printf("fail\n");
     399           0 :                         fails++;
     400             :                 }
     401          24 :                 i++;
     402          24 :                 ODBC_FREE();
     403             :         }
     404             : 
     405           8 :         if (fails == 0) {
     406           8 :                 printf("\nAll %d tests passed.\n\n", passes);
     407             :         } else {
     408           0 :                 printf("\nTest passes: %d, test fails: %d\n\n", passes, fails);
     409             :         }
     410             : 
     411             :         /* Return true if there are no failures */
     412           8 :         return (fails == 0);
     413             : }
     414             : 
     415             : int
     416           8 : main(void)
     417             : {
     418           8 :         odbc_use_version3 = true;
     419             : 
     420           8 :         if (RunTests())
     421             :                 return 0;       /* Success */
     422           0 :         return 1;       /* Error code */
     423             : }

Generated by: LCOV version 1.13