1 #if !defined(ODBCXX_QT)
2 
3 #include <odbc++/drivermanager.h>
4 #include <odbc++/connection.h>
5 #include <odbc++/resultset.h>
6 #include <odbc++/resultsetmetadata.h>
7 #include <odbc++/callablestatement.h>
8 #include <odbc++/databasemetadata.h>
9 
10 #include <sstream>
11 #include <iostream>
12 #include <memory>
13 
14 using namespace std;
15 using namespace odbc;
16 
17 // note: this must be even
18 const int TABLE_ROWS=500;
19 
20 #define PREFIX ODBCXX_STRING_CONST("odbcxx_")
21 
22 const ODBCXX_STRING tableName(PREFIX ODBCXX_STRING_CONST("test"));
23 const ODBCXX_STRING procName(PREFIX ODBCXX_STRING_CONST("ptest"));
24 const ODBCXX_STRING funcName(PREFIX ODBCXX_STRING_CONST("ftest"));
25 
26 int assertionsFailed=0;
27 
28 #define ASSERT(x)                                              \
29 do {                                                           \
30   if(!(x)) {                                                   \
31     ODBCXX_CERR << ODBCXX_STRING_CONST("Assertion \"") << #x   \
32                 << ODBCXX_STRING_CONST("\" failed") << endl;   \
33     assertionsFailed++;                                        \
34   }                                                            \
35 } while(false)
36 
dumpWarnings(Statement * stmt)37 static void dumpWarnings(Statement* stmt)
38 {
39   std::auto_ptr<WarningList> warnings
40     =std::auto_ptr<WarningList>(stmt->getWarnings());
41   for(WarningList::iterator i=warnings->begin();
42       i!=warnings->end(); i++) {
43     ODBCXX_COUT << ODBCXX_STRING_CONST("Warning: ")
44                 << (*i)->getMessage() << endl;
45   }
46 }
47 
dropStuff(Connection * con)48 static void dropStuff(Connection* con)
49 {
50   std::auto_ptr<Statement> stmt=std::auto_ptr<Statement>(con->createStatement());
51   try {
52     stmt->executeUpdate(ODBCXX_STRING_CONST("drop table ")+tableName);
53     ODBCXX_COUT << ODBCXX_STRING_CONST("Dropped table ") << tableName << endl;
54     dumpWarnings(stmt.get());
55   } catch(SQLException& e) {}
56 
57   try {
58     stmt->executeUpdate(ODBCXX_STRING_CONST("drop procedure ")+procName);
59     ODBCXX_COUT << ODBCXX_STRING_CONST("Dropped procedure ") << procName << endl;
60     dumpWarnings(stmt.get());
61   } catch(SQLException& e) {}
62 
63   try {
64     stmt->executeUpdate(ODBCXX_STRING_CONST("drop function ")+funcName);
65     ODBCXX_COUT << ODBCXX_STRING_CONST("Dropped function ") << funcName << endl;
66     dumpWarnings(stmt.get());
67   } catch(SQLException& e) {}
68 }
69 
createStuff(Connection * con)70 static void createStuff(Connection* con)
71 {
72   std::auto_ptr<Statement> stmt=std::auto_ptr<Statement>(con->createStatement());
73 
74   // create the table
75   stmt->executeUpdate(ODBCXX_STRING_CONST("create table ")+tableName+
76                       ODBCXX_STRING_CONST(" (id number(4) not null primary key, ")
77                       ODBCXX_STRING_CONST("name varchar2(22) not null, ")
78                       ODBCXX_STRING_CONST("ts date)"));
79 
80   dumpWarnings(stmt.get());
81   ODBCXX_COUT << ODBCXX_STRING_CONST("Created table ") << tableName << endl;
82 
83   // create the procedure
84   stmt->executeUpdate
85     (ODBCXX_STRING_CONST("create procedure ")+procName+
86      ODBCXX_STRING_CONST(" (a in integer, b out integer, s in out varchar2) as ")
87      ODBCXX_STRING_CONST("begin ")
88      ODBCXX_STRING_CONST("  b:=a*2; ")
89      ODBCXX_STRING_CONST("  s:= s || ': ' || a || '*2=' || b; ")
90      ODBCXX_STRING_CONST("end;"));
91 
92   dumpWarnings(stmt.get());
93   ODBCXX_COUT << ODBCXX_STRING_CONST("Created procedure ") << procName << endl;
94 
95   // create the function
96   stmt->executeUpdate
97     (ODBCXX_STRING_CONST("create function ")+funcName+
98      ODBCXX_STRING_CONST(" (a in number, s in out varchar2) ")
99      ODBCXX_STRING_CONST("return number as ")
100      ODBCXX_STRING_CONST("b number; ")
101      ODBCXX_STRING_CONST("begin ")
102      ODBCXX_STRING_CONST("  b:=a*2; ")
103      ODBCXX_STRING_CONST("  s:= s || ': ' || a || '*2=' || b; ")
104      ODBCXX_STRING_CONST("  return b; ")
105      ODBCXX_STRING_CONST("end;"));
106 
107   dumpWarnings(stmt.get());
108   ODBCXX_COUT << ODBCXX_STRING_CONST("Created function ") << funcName << endl;
109 }
110 
testProc(Connection * con)111 static void testProc(Connection* con)
112 {
113   std::auto_ptr<CallableStatement> stmt=std::auto_ptr<CallableStatement>(con->prepareCall
114     (ODBCXX_STRING_CONST("{call ")+procName+ODBCXX_STRING_CONST("(?,?,?)}")));
115   stmt->setInt(1,22);
116   stmt->registerOutParameter(2,Types::INTEGER);
117   stmt->setString(3, ODBCXX_STRING_CONST("Okay"));
118   stmt->executeUpdate();
119 
120   ASSERT(stmt->getInt(2)==44);
121   ASSERT(stmt->getString(3)==ODBCXX_STRING_CONST("Okay: 22*2=44"));
122 }
123 
testFunc(Connection * con)124 static void testFunc(Connection* con)
125 {
126   std::auto_ptr<CallableStatement> stmt=std::auto_ptr<CallableStatement>(con->prepareCall
127     (ODBCXX_STRING_CONST("{?=call ")+procName+ODBCXX_STRING_CONST("(?,?)}")));
128 
129   stmt->registerOutParameter(1,Types::INTEGER);
130   stmt->setInt(2,22);
131   stmt->setString(3,ODBCXX_STRING_CONST("Okay"));
132   stmt->executeUpdate();
133 
134   ASSERT(stmt->getInt(1)==44);
135   ASSERT(stmt->getString(3)==ODBCXX_STRING_CONST("Okay: 22*2=44"));
136 }
137 
138 
testTable(Connection * con)139 static void testTable(Connection* con)
140 {
141   int i=0;
142   int driverVersion=con->getMetaData()->getDriverMajorVersion();
143 
144   if(driverVersion<3) {
145     // insert the first row using a prepared statement
146     // ODBC 2 drivers can't do inserts before a fetch is done.
147     // some can't do inserts if the result set is not on a
148     // real row.
149     std::auto_ptr<PreparedStatement> pstmt
150       =std::auto_ptr<PreparedStatement>(con->prepareStatement
151       (ODBCXX_STRING_CONST("insert into ")+tableName+
152        ODBCXX_STRING_CONST(" (id,name,ts) values(?,?,?)")));
153     pstmt->setInt(1,i);
154     pstmt->setString(2,ODBCXX_STRING_CONST("This is row number 0"));
155     {
156       Timestamp ts;
157       pstmt->setTimestamp(3,ts);
158     }
159     pstmt->executeUpdate();
160     ODBCXX_COUT << ODBCXX_STRING_CONST("Inserted row 0") << endl;
161     i++;
162   }
163 
164   // populate our table using a ResultSet
165   std::auto_ptr<Statement> stmt=std::auto_ptr<Statement>(con->createStatement
166     (ResultSet::TYPE_SCROLL_SENSITIVE, ResultSet::CONCUR_UPDATABLE));
167 
168   // set fetch size to something useful
169   stmt->setFetchSize(10);
170 
171   std::auto_ptr<ResultSet> rs=std::auto_ptr<ResultSet>
172     (stmt->executeQuery(ODBCXX_STRING_CONST("select id,name,ts from ")+tableName));
173 
174   if(driverVersion<3) {
175     // position ourselves on a real row
176     ASSERT(rs->next());
177   }
178 
179   rs->moveToInsertRow();
180 
181   while(i<TABLE_ROWS) {
182     basic_ostringstream<ODBCXX_CHAR_TYPE> ns;
183     ns << ODBCXX_STRING_CONST("This is row number ") << i;
184     ODBCXX_STRING name(ns.str());
185     rs->updateInt(1,i);
186     rs->updateString(2,name);
187     rs->updateTimestamp(3,Timestamp());
188     rs->insertRow();
189     ODBCXX_COUT << ODBCXX_STRING_CONST("Inserted row ") << i << endl;
190     i++;
191   }
192   rs->moveToCurrentRow();
193 
194   con->commit();
195 
196   rs=std::auto_ptr<ResultSet>(stmt->executeQuery
197     (ODBCXX_STRING_CONST("select id,name from ")+tableName));
198 
199   i=0;
200   while(rs->next()) {
201     ODBCXX_COUT << ODBCXX_STRING_CONST("Checking row ") << i << endl;
202     basic_ostringstream<ODBCXX_CHAR_TYPE> ns;
203     ns << ODBCXX_STRING_CONST("This is row number ") << i;
204     ODBCXX_STRING name(ns.str());
205     ASSERT(rs->getString(ODBCXX_STRING_CONST("name"))==name);
206 
207     ASSERT(rs->getInt(ODBCXX_STRING_CONST("id"))==i);
208     i++;
209   }
210 
211   ODBCXX_COUT << ODBCXX_STRING_CONST("Check done") << endl;
212 
213   rs=std::auto_ptr<ResultSet>(stmt->executeQuery
214     (ODBCXX_STRING_CONST("select id,name,ts from ")+tableName));
215   i=0;
216   while(rs->next()) {
217     if((i%2)==1) {
218       basic_ostringstream<ODBCXX_CHAR_TYPE> ns;
219       ns << ODBCXX_STRING_CONST("This IS row number ") << i;
220       ODBCXX_STRING name(ns.str());
221 
222       rs->updateString(ODBCXX_STRING_CONST("name"),name);
223       Timestamp ts;
224       rs->updateTimestamp(ODBCXX_STRING_CONST("ts"),ts);
225       rs->updateRow();
226       ODBCXX_COUT << ODBCXX_STRING_CONST("Updated row ") << i << endl;
227     } else {
228       rs->deleteRow();
229       ODBCXX_COUT << ODBCXX_STRING_CONST("Deleted row ") << i << endl;
230     }
231     i++;
232   }
233 
234   rs=std::auto_ptr<ResultSet>(stmt->executeQuery
235     (ODBCXX_STRING_CONST("select id,name,ts from ")+tableName));
236   i=1;
237   while(rs->next()) {
238     basic_ostringstream<ODBCXX_CHAR_TYPE> ns;
239     ns << ODBCXX_STRING_CONST("This IS row number ") << i;
240     ODBCXX_STRING name(ns.str());
241 
242     ASSERT(rs->getString(ODBCXX_STRING_CONST("name"))==name);
243     ASSERT(rs->getInt(1)==i);
244 
245     i+=2;
246   }
247 
248   ASSERT(i==TABLE_ROWS+1);
249 
250   con->commit();
251 }
252 
253 
main(int argc,char ** argv)254 int main(int argc, char** argv)
255 {
256   if(argc!=2 && argc!=4) {
257     cerr << "Usage: " << argv[0] << " connect-string" << endl
258          << "or     " << argv[0] << " dsn username password" << endl;
259     return 0;
260   }
261   try {
262     std::vector<ODBCXX_STRING> vargv(argc-1);
263     const size_t MAX_CHARS = 256;
264     for(int i=1;i<argc;++i)
265     {
266       ODBCXX_STRING& arg=vargv[i-1];
267 #if defined(ODBCXX_UNICODE)
268       wchar_t buffer[MAX_CHARS];
269       size_t len=mbstowcs(buffer,argv[i],MAX_CHARS);
270       if(0<len&&MAX_CHARS>len)
271       {
272          arg=buffer;
273       }
274 #else
275       arg=argv[i];
276 #endif
277     }
278     std::auto_ptr<Connection> con;
279     if(argc==2) {
280       ODBCXX_COUT << ODBCXX_STRING_CONST("Connecting to ") << vargv[0]
281                   << ODBCXX_STRING_CONST("...") << flush;
282       con=std::auto_ptr<Connection>(DriverManager::getConnection(vargv[0]));
283     } else {
284       ODBCXX_COUT << ODBCXX_STRING_CONST("Connecting to dsn=") << vargv[0]
285                   << ODBCXX_STRING_CONST(", uid=") << vargv[1]
286                   << ODBCXX_STRING_CONST(", pwd=") << vargv[2]
287                   << ODBCXX_STRING_CONST("...") << flush;
288       con=std::auto_ptr<Connection>(DriverManager::getConnection(vargv[0],vargv[1],vargv[2]));
289     }
290     ODBCXX_COUT << ODBCXX_STRING_CONST(" done.") << endl;
291 
292     int numTests=3;
293     int failedTests=0;
294 
295     con->setAutoCommit(false);
296     dropStuff(con.get());
297     createStuff(con.get());
298     try {
299       testProc(con.get());
300     } catch(SQLException& e) {
301       ODBCXX_COUT << ODBCXX_STRING_CONST("Procedure test FAILED: ")
302                   << e.getMessage() << endl;
303       failedTests++;
304     }
305 
306     try {
307       testFunc(con.get());
308     } catch(SQLException& e) {
309       ODBCXX_COUT << ODBCXX_STRING_CONST("Function test FAILED: ")
310                   << e.getMessage() << endl;
311       failedTests++;
312     }
313 
314     try {
315       testTable(con.get());
316     } catch(SQLException& e) {
317       ODBCXX_COUT << ODBCXX_STRING_CONST("Table test FAILED: ")
318                   << e.getMessage() << endl;
319       failedTests++;
320     }
321 
322     dropStuff(con.get());
323 
324     if(failedTests>0) {
325       ODBCXX_COUT << failedTests << ODBCXX_STRING_CONST(" of ") << numTests
326                   << ODBCXX_STRING_CONST(" tests failed.") << endl;
327     }
328   } catch(exception& e) {
329     cout << "Whoops: " << e.what() << endl;
330     return 1;
331   }
332 
333   if(assertionsFailed>0) {
334     ODBCXX_COUT << assertionsFailed << ODBCXX_STRING_CONST(" assertions failed.") << endl;
335     return 2;
336   }
337   return 0;
338 }
339 
340 #else
main()341 int main() { return 0; }
342 #endif
343