LCOV - code coverage report
Current view: top level - src/odbc/unittests - testodbc.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 140 152 92.1 %
Date: 2025-02-21 09:36:06 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          10 : TestRawODBCPreparedQuery(void)
      32             : {
      33             :         SQLTCHAR *queryString;
      34          10 :         SQLLEN lenOrInd = 0;
      35          10 :         SQLSMALLINT supplierId = 4;
      36             :         int count;
      37             : 
      38          10 :         AB_FUNCT(("TestRawODBCPreparedQuery (in)"));
      39             : 
      40             :         /* INIT */
      41             : 
      42          10 :         odbc_connect();
      43             : 
      44             :         /* MAKE QUERY */
      45             : 
      46          10 :         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          60 :         while (SQLMoreResults(odbc_stmt) == SQL_SUCCESS)
      64          40 :                 continue;
      65             : 
      66          10 :         queryString = T("SELECT * FROM #Products WHERE SupplierID = ?");
      67             : 
      68          10 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &supplierId, 0, &lenOrInd, "S");
      69             : 
      70          10 :         CHKPrepare(queryString, SQL_NTS, "S");
      71             : 
      72          10 :         CHKExecute("S");
      73             : 
      74          10 :         count = 0;
      75             : 
      76          50 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
      77          30 :                 count++;
      78             :         }
      79          10 :         AB_PRINT(("Got %d rows", count));
      80             : 
      81          10 :         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          10 :         odbc_disconnect();
      94             : 
      95          10 :         AB_FUNCT(("TestRawODBCPreparedQuery (out): ok"));
      96          10 :         return true;
      97             : }
      98             : 
      99             : /*
     100             :  * Test that makes a parameterized ODBC query using SQLExecDirect.
     101             :  */
     102             : static bool
     103          10 : TestRawODBCDirectQuery(void)
     104             : {
     105          10 :         SQLLEN lenOrInd = 0;
     106          10 :         SQLSMALLINT supplierId = 1;
     107             :         int count;
     108             : 
     109          10 :         AB_FUNCT(("TestRawODBCDirectQuery (in)"));
     110             : 
     111             :         /* INIT */
     112             : 
     113          10 :         odbc_connect();
     114             : 
     115             :         /* MAKE QUERY */
     116             : 
     117          10 :         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          60 :         while (SQLMoreResults(odbc_stmt) == SQL_SUCCESS)
     135          40 :                 continue;
     136             : 
     137          10 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &supplierId, 0, &lenOrInd, "S");
     138             : 
     139          10 :         CHKExecDirect(T("SELECT * FROM #Products WHERE SupplierID = ?"), SQL_NTS, "S");
     140             : 
     141          10 :         count = 0;
     142             : 
     143          50 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     144          30 :                 count++;
     145             :         }
     146          10 :         AB_PRINT(("Got %d rows", count));
     147             : 
     148          10 :         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          10 :         odbc_disconnect();
     161             : 
     162          10 :         AB_FUNCT(("TestRawODBCDirectQuery (out): ok"));
     163          10 :         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          10 : 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             : 
     183          10 :         AB_FUNCT(("TestRawODBCGuid (in)"));
     184             : 
     185          10 :         odbc_connect();
     186             :         
     187          10 :         if (!odbc_db_is_microsoft()) {
     188           2 :                 odbc_disconnect();
     189           2 :                 return true;
     190             :         }
     191             : 
     192           8 :         AB_PRINT(("Creating #pet table"));
     193             : 
     194           8 :         queryString = "CREATE TABLE #pet (name VARCHAR(20), owner VARCHAR(20), "
     195             :                "species VARCHAR(20), sex CHAR(1), age INTEGER, " "guid UNIQUEIDENTIFIER DEFAULT NEWID() ); ";
     196           8 :         CHKExecDirect(T(queryString), SQL_NTS, "SNo");
     197             : 
     198           8 :         odbc_command_with_result(odbc_stmt, "DROP PROC GetGUIDRows");
     199             : 
     200           8 :         AB_PRINT(("Creating stored proc GetGUIDRows"));
     201             : 
     202           8 :         queryString = "CREATE PROCEDURE GetGUIDRows (@guidpar uniqueidentifier) AS \
     203             :                 SELECT name, guid FROM #pet WHERE guid = @guidpar";
     204           8 :         CHKExecDirect(T(queryString), SQL_NTS, "SNo");
     205             : 
     206           8 :         AB_PRINT(("Insert row 1"));
     207             : 
     208           8 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age ) \
     209             :                          VALUES ( 'Fang', 'Mike', 'dog', 'm', 12 );";
     210           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     211             : 
     212           8 :         AB_PRINT(("Insert row 2"));
     213             : 
     214             :         /*
     215             :          * Ok - new row with explicit GUID, but parameterised age.
     216             :          */
     217           8 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     218             :                          VALUES ( 'Splash', 'Dan', 'fish', 'm', ?, \
     219             :                          '12345678-1234-1234-1234-123456789012' );";
     220             : 
     221           8 :         lenOrInd = 0;
     222           8 :         age = 3;
     223           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &age, 0, &lenOrInd, "S");
     224             : 
     225           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     226           8 :         CHKFreeStmt(SQL_CLOSE, "S");
     227             : 
     228           8 :         AB_PRINT(("Insert row 3"));
     229             :         /*
     230             :          * Ok - new row with parameterised GUID.
     231             :          */
     232           8 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     233             :                          VALUES ( 'Woof', 'Tom', 'cat', 'f', 2, ? );";
     234             : 
     235           8 :         lenOrInd = SQL_NTS;
     236           8 :         strcpy(guid, "87654321-4321-4321-4321-123456789abc");
     237             : 
     238           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_GUID, 0, 0, guid, 0, &lenOrInd, "S");
     239           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     240             : 
     241           8 :         AB_PRINT(("Insert row 4"));
     242             :         /*
     243             :          * Ok - new row with parameterised GUID.
     244             :          */
     245           8 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     246             :                          VALUES ( 'Spike', 'Diane', 'pig', 'f', 4, ? );";
     247             : 
     248           8 :         lenOrInd = SQL_NTS;
     249           8 :         strcpy(guid, "1234abcd-abcd-abcd-abcd-123456789abc");
     250             : 
     251           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 36, 0, guid, 0, &lenOrInd, "S");
     252           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     253             : 
     254           8 :         AB_PRINT(("Insert row 5"));
     255             :         /*
     256             :          * Ok - new row with parameterised GUID.
     257             :          */
     258           8 :         queryString = "INSERT INTO #pet( name, owner, species, sex, age, guid ) \
     259             :                          VALUES ( 'Fluffy', 'Sam', 'dragon', 'm', 16, ? );";
     260             : 
     261           8 :         sqlguid.Data1 = 0xaabbccdd;
     262           8 :         sqlguid.Data2 = 0xeeff;
     263           8 :         sqlguid.Data3 = 0x1122;
     264           8 :         sqlguid.Data4[0] = 0x11;
     265           8 :         sqlguid.Data4[1] = 0x22;
     266           8 :         sqlguid.Data4[2] = 0x33;
     267           8 :         sqlguid.Data4[3] = 0x44;
     268           8 :         sqlguid.Data4[4] = 0x55;
     269           8 :         sqlguid.Data4[5] = 0x66;
     270           8 :         sqlguid.Data4[6] = 0x77;
     271           8 :         sqlguid.Data4[7] = 0x88;
     272             : 
     273           8 :         lenOrInd = 16;
     274           8 :         strcpy(guid, "1234abcd-abcd-abcd-abcd-123456789abc");
     275             : 
     276           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID, 16, 0, &sqlguid, 16, &lenOrInd, "S");
     277           8 :         status = SQLExecDirect(odbc_stmt, T(queryString), SQL_NTS);
     278           8 :         if (status != SQL_SUCCESS) {
     279           0 :                 AB_ERROR(("Insert row 5 failed"));
     280           0 :                 AB_ERROR(("Sadly this was expected in *nix ODBC. Carry on."));
     281             :         }
     282             : 
     283             :         /*
     284             :          * Now retrieve rows - especially GUID column values.
     285             :          */
     286           8 :         AB_PRINT(("retrieving name and guid"));
     287           8 :         queryString = "SELECT name, guid FROM #pet";
     288           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     289          56 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     290          40 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     291          40 :                 CHKGetData(2, SQL_CHAR, guid, 37, 0, "S");
     292             : 
     293          40 :                 AB_PRINT(("name: %-10s guid: %s", name, guid));
     294             :         }
     295             : 
     296             :         /*
     297             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     298             :          * state if we try SELECT again).
     299             :          */
     300           8 :         odbc_reset_statement();
     301             : 
     302             : 
     303             :         /*
     304             :          * Now retrieve rows - especially GUID column values.
     305             :          */
     306             : 
     307           8 :         AB_PRINT(("retrieving name and guid again"));
     308           8 :         queryString = "SELECT name, guid FROM #pet";
     309           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     310          56 :         while (CHKFetch("SNo") == SQL_SUCCESS) {
     311          40 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     312          40 :                 CHKGetData(2, SQL_GUID, &sqlguid, 16, 0, "S");
     313             : 
     314          40 :                 AB_PRINT(("%-10s %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
     315             :                           name,
     316             :                           (int) (sqlguid.Data1), sqlguid.Data2,
     317             :                           sqlguid.Data3, sqlguid.Data4[0], sqlguid.Data4[1],
     318             :                           sqlguid.Data4[2], sqlguid.Data4[3], sqlguid.Data4[4],
     319             :                           sqlguid.Data4[5], sqlguid.Data4[6], sqlguid.Data4[7]));
     320             :         }
     321             : 
     322             :         /*
     323             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     324             :          * state if we try SELECT again).
     325             :          */
     326           8 :         odbc_reset_statement();
     327             : 
     328             :         /*
     329             :          * Now retrieve rows via stored procedure passing GUID as param.
     330             :          */
     331           8 :         AB_PRINT(("retrieving name and guid"));
     332             : 
     333           8 :         queryString = "{call GetGUIDRows(?)}";
     334           8 :         lenOrInd = SQL_NTS;
     335           8 :         strcpy(guid, "87654321-4321-4321-4321-123456789abc");
     336             : 
     337           8 :         CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_GUID, 0, 0, guid, 0, &lenOrInd, "S");
     338           8 :         CHKExecDirect(T(queryString), SQL_NTS, "S");
     339          24 :         while (SQLFetch(odbc_stmt) == SQL_SUCCESS) {
     340           8 :                 CHKGetData(1, SQL_CHAR, name, 20, 0, "S");
     341           8 :                 CHKGetData(2, SQL_CHAR, guid, 37, 0, "S");
     342             : 
     343           8 :                 AB_PRINT(("%-10s %s", name, guid));
     344             :         }
     345             : 
     346             :         /*
     347             :          * Realloc cursor handle - (Windows ODBC considers it an invalid cursor
     348             :          * state after a previous SELECT has occurred).
     349             :          */
     350           8 :         odbc_reset_statement();
     351             : 
     352             :         /* cleanup */
     353           8 :         odbc_command_with_result(odbc_stmt, "DROP PROC GetGUIDRows");
     354             : 
     355             :         /* CLOSEDOWN */
     356             : 
     357           8 :         odbc_disconnect();
     358             : 
     359           8 :         AB_FUNCT(("TestRawODBCGuid (out): ok"));
     360           8 :         return true;
     361             : }
     362             : 
     363             : /**
     364             :  * Array of tests.
     365             :  */
     366             : static const DbTestEntry tests[] = {
     367             :         /* 1 */ {TestRawODBCDirectQuery, "Raw ODBC direct query"},
     368             :         /* 2 */ {TestRawODBCPreparedQuery, "Raw ODBC prepared query"},
     369             :         /* 3 */ {TestRawODBCGuid, "Raw ODBC GUID"},
     370             :         /* end */ {NULL, NULL}
     371             : };
     372             : 
     373             : /**
     374             :  * Code to iterate through all tests to run.
     375             :  *
     376             :  * \return
     377             :  *      true if all tests pass, false if any tests fail.
     378             :  */
     379             : static bool
     380          10 : RunTests(void)
     381             : {
     382             :         unsigned int i;
     383          10 :         unsigned int passes = 0;
     384          10 :         unsigned int fails = 0;
     385             : 
     386          10 :         i = 0;
     387          50 :         while (tests[i].testFn) {
     388          30 :                 printf("Running test %2d: %s... ", i + 1, tests[i].description);
     389          30 :                 fflush(stdout);
     390          30 :                 if (tests[i].testFn()) {
     391          30 :                         printf("pass\n");
     392          30 :                         passes++;
     393             :                 } else {
     394           0 :                         printf("fail\n");
     395           0 :                         fails++;
     396             :                 }
     397          30 :                 i++;
     398          30 :                 ODBC_FREE();
     399             :         }
     400             : 
     401          10 :         if (fails == 0) {
     402          10 :                 printf("\nAll %d tests passed.\n\n", passes);
     403             :         } else {
     404           0 :                 printf("\nTest passes: %d, test fails: %d\n\n", passes, fails);
     405             :         }
     406             : 
     407             :         /* Return true if there are no failures */
     408          10 :         return (fails == 0);
     409             : }
     410             : 
     411             : int
     412          10 : main(void)
     413             : {
     414          10 :         odbc_use_version3 = true;
     415             : 
     416          10 :         if (RunTests())
     417             :                 return 0;       /* Success */
     418           0 :         return 1;       /* Error code */
     419             : }

Generated by: LCOV version 1.13