1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestSQLiteDatabase.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 /*-------------------------------------------------------------------------
16   Copyright 2008 Sandia Corporation.
17   Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
18   the U.S. Government retains certain rights in this software.
19 -------------------------------------------------------------------------*/
20 // .SECTION Thanks
21 // Thanks to Andrew Wilson from Sandia National Laboratories for implementing
22 // this test.
23 
24 #include "DatabaseSchemaWith2Tables.h"
25 #include "vtkRowQueryToTable.h"
26 #include "vtkSQLDatabaseSchema.h"
27 #include "vtkSQLQuery.h"
28 #include "vtkSQLiteDatabase.h"
29 #include "vtkSmartPointer.h"
30 #include "vtkStdString.h"
31 #include "vtkTable.h"
32 #include "vtkVariant.h"
33 #include "vtkVariantArray.h"
34 
35 #include "vtkTestErrorObserver.h"
36 
37 #include <vector>
38 
TestSQLiteDatabase(int,char * [])39 int TestSQLiteDatabase(int /*argc*/, char* /*argv*/[])
40 {
41   bool status;
42 
43   cerr << ">>>>> Testing bad input." << endl;
44 
45   vtkSQLDatabase* db0 = vtkSQLDatabase::CreateFromURL(nullptr);
46   if (db0)
47   {
48     cerr << "ERROR: Created a database from a nullptr URL! How?" << endl;
49     db0->Delete();
50     return 1;
51   }
52 
53   cerr << ">>>>> Testing creation modes." << endl;
54 
55   vtkSmartPointer<vtkTest::ErrorObserver> errorObserver =
56     vtkSmartPointer<vtkTest::ErrorObserver>::New();
57   vtkSmartPointer<vtkTest::ErrorObserver> queryObserver =
58     vtkSmartPointer<vtkTest::ErrorObserver>::New();
59 
60   vtkSQLiteDatabase* db1 =
61     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://local.db"));
62 
63   status = db1->Open("", vtkSQLiteDatabase::CREATE_OR_CLEAR);
64   vtkSQLQuery* query1 = db1->GetQueryInstance();
65   query1->SetQuery("CREATE TABLE test (id INTEGER)");
66   if (!query1->Execute())
67   {
68     cerr << "Create query failed" << endl;
69     return 1;
70   }
71   if (!status)
72   {
73     cerr << "Couldn't open database using CREATE_OR_CLEAR.\n";
74     return 1;
75   }
76   db1->Delete();
77   query1->Delete();
78 
79   vtkSQLiteDatabase* db2 =
80     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://local.db"));
81   db2->AddObserver(vtkCommand::ErrorEvent, errorObserver);
82   status = db2->Open("", vtkSQLiteDatabase::CREATE);
83   if (status)
84   {
85     cerr << "Using CREATE on an existing file should have failed but did not.\n";
86     return 1;
87   }
88   int status1 =
89     errorObserver->CheckErrorMessage("You specified creating a database but the file exists");
90   db2->Delete();
91 
92   vtkSQLiteDatabase* db3 =
93     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://local.db"));
94   status = db3->Open("", vtkSQLiteDatabase::USE_EXISTING_OR_CREATE);
95   if (!status)
96   {
97     cerr << "Using USE_EXISTING_OR_CREATE did not work.\n";
98     return 1;
99   }
100   vtkSQLQuery* query3 = db3->GetQueryInstance();
101   query3->SetQuery("SELECT * from test");
102   if (!query3->Execute())
103   {
104     cerr << "Select query failed" << endl;
105     return 1;
106   }
107   db3->Delete();
108   query3->Delete();
109 
110   vtkSQLiteDatabase* db4 =
111     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://local.db"));
112   status = db4->Open("", vtkSQLiteDatabase::CREATE_OR_CLEAR);
113   if (!status)
114   {
115     cerr << "Using CREATE_OR_CLEAR did not work.\n";
116     return 1;
117   }
118   vtkSQLQuery* query4 = db4->GetQueryInstance();
119   query4->AddObserver(vtkCommand::ErrorEvent, queryObserver);
120   query4->SetQuery("SELECT * from test");
121   if (query4->Execute())
122   {
123     cerr << "Select query succeeded when it shouldn't have." << endl;
124     return 1;
125   }
126   status1 += queryObserver->CheckErrorMessage("Query is not null but prepared statement is");
127   db4->Delete();
128   query4->Delete();
129 
130   cerr << ">>>>> Testing database functions" << endl;
131 
132   vtkSQLiteDatabase* db =
133     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://:memory:"));
134   status = db->Open("");
135 
136   if (!status)
137   {
138     cerr << "Couldn't open database.\n";
139     return 1;
140   }
141 
142   vtkSQLQuery* query = db->GetQueryInstance();
143 
144   vtkStdString createQuery(
145     "CREATE TABLE IF NOT EXISTS people (name TEXT, age INTEGER, weight FLOAT)");
146   cout << createQuery << endl;
147   query->SetQuery(createQuery.c_str());
148   if (!query->Execute())
149   {
150     cerr << "Create query failed" << endl;
151     return 1;
152   }
153 
154   int i;
155   for (i = 0; i < 20; i++)
156   {
157     char insertQuery[200];
158     snprintf(insertQuery, sizeof(insertQuery),
159       "INSERT INTO people (name, age, weight) VALUES('John Doe %d', %d, %f)", i, i, 10.1 * i);
160     cout << insertQuery << endl;
161     query->SetQuery(insertQuery);
162     if (!query->Execute())
163     {
164       cerr << "Insert query " << i << " failed" << endl;
165       return 1;
166     }
167   }
168 
169   const char* placeholders = "INSERT INTO people (name, age, weight) VALUES (?, ?, ?)";
170   query->SetQuery(placeholders);
171   for (i = 21; i < 40; i++)
172   {
173     char name[20];
174     snprintf(name, sizeof(name), "John Doe %d", i);
175     bool bind1 = query->BindParameter(0, name);
176     bool bind2 = query->BindParameter(1, i);
177     bool bind3 = query->BindParameter(2, 10.1 * i);
178     if (!(bind1 && bind2 && bind3))
179     {
180       cerr << "Parameter binding failed on query " << i << ": " << bind1 << " " << bind2 << " "
181            << bind3 << endl;
182       return 1;
183     }
184     cout << query->GetQuery() << endl;
185     if (!query->Execute())
186     {
187       cerr << "Insert query " << i << " failed" << endl;
188       return 1;
189     }
190   }
191 
192   const char* queryText = "SELECT name, age, weight FROM people WHERE age <= 20";
193   query->SetQuery(queryText);
194   cerr << endl << "Running query: " << query->GetQuery() << endl;
195 
196   cerr << endl << "Using vtkSQLQuery directly to execute query:" << endl;
197   if (!query->Execute())
198   {
199     cerr << "Query failed" << endl;
200     return 1;
201   }
202 
203   for (int col = 0; col < query->GetNumberOfFields(); col++)
204   {
205     if (col > 0)
206     {
207       cerr << ", ";
208     }
209     cerr << query->GetFieldName(col);
210   }
211   cerr << endl;
212   while (query->NextRow())
213   {
214     for (int field = 0; field < query->GetNumberOfFields(); field++)
215     {
216       if (field > 0)
217       {
218         cerr << ", ";
219       }
220       cerr << query->DataValue(field).ToString().c_str();
221     }
222     cerr << endl;
223   }
224 
225   cerr << endl << "Using vtkSQLQuery to execute query and retrieve by row:" << endl;
226   if (!query->Execute())
227   {
228     cerr << "Query failed" << endl;
229     return 1;
230   }
231   for (int col = 0; col < query->GetNumberOfFields(); col++)
232   {
233     if (col > 0)
234     {
235       cerr << ", ";
236     }
237     cerr << query->GetFieldName(col);
238   }
239   cerr << endl;
240   vtkVariantArray* va = vtkVariantArray::New();
241   while (query->NextRow(va))
242   {
243     for (int field = 0; field < va->GetNumberOfValues(); field++)
244     {
245       if (field > 0)
246       {
247         cerr << ", ";
248       }
249       cerr << va->GetValue(field).ToString().c_str();
250     }
251     cerr << endl;
252   }
253   va->Delete();
254 
255   cerr << endl << "Using vtkRowQueryToTable to execute query:" << endl;
256   vtkRowQueryToTable* reader = vtkRowQueryToTable::New();
257   reader->SetQuery(query);
258   reader->Update();
259   vtkTable* table = reader->GetOutput();
260   for (vtkIdType col = 0; col < table->GetNumberOfColumns(); col++)
261   {
262     table->GetColumn(col)->Print(cerr);
263   }
264   cerr << endl;
265   for (vtkIdType row = 0; row < table->GetNumberOfRows(); row++)
266   {
267     for (vtkIdType col = 0; col < table->GetNumberOfColumns(); col++)
268     {
269       vtkVariant v = table->GetValue(row, col);
270       cerr << "row " << row << ", col " << col << " - " << v.ToString() << " ("
271            << vtkImageScalarTypeNameMacro(v.GetType()) << ")" << endl;
272     }
273   }
274 
275   reader->Delete();
276   query->Delete();
277   db->Delete();
278 
279   // ----------------------------------------------------------------------
280   // Testing transformation of a schema into a SQLite database
281 
282   // 1. Create the schema
283   DatabaseSchemaWith2Tables schema;
284 
285   // 2. Convert the schema into a SQLite database
286   cerr << "@@ Converting the schema into a SQLite database...";
287 
288   vtkSQLiteDatabase* dbSch =
289     vtkSQLiteDatabase::SafeDownCast(vtkSQLDatabase::CreateFromURL("sqlite://:memory:"));
290   status = dbSch->Open("");
291 
292   if (!status)
293   {
294     cerr << "Couldn't open database.\n";
295     return 1;
296   }
297 
298   status = dbSch->EffectSchema(schema.GetSchema());
299   if (!status)
300   {
301     cerr << "Could not effect test schema.\n";
302     return 1;
303   }
304   cerr << " done." << endl;
305 
306   // 3. Count tables of the newly created database
307   cerr << "@@ Fetching table names of the newly created database:\n";
308 
309   query = dbSch->GetQueryInstance();
310 
311   query->SetQuery("SELECT name FROM sqlite_master WHERE type = \"table\"");
312   if (!query->Execute())
313   {
314     cerr << "Query failed" << endl;
315     return 1;
316   }
317 
318   std::vector<vtkStdString> tables;
319   int tblHandle = 0;
320   for (; query->NextRow(); ++tblHandle)
321   {
322     vtkStdString tblNameSch(schema->GetTableNameFromHandle(tblHandle));
323     vtkStdString tblNameDB(query->DataValue(0).ToString());
324     cerr << "     " << tblNameDB << "\n";
325 
326     if (tblNameDB != tblNameSch)
327     {
328       cerr << "Fetched an incorrect name: " << tblNameDB << " != " << tblNameSch << endl;
329       return 1;
330     }
331 
332     tables.push_back(tblNameDB);
333   }
334 
335   if (tblHandle != schema->GetNumberOfTables())
336   {
337     cerr << "Found an incorrect number of tables: " << tblHandle
338          << " != " << schema->GetNumberOfTables() << endl;
339     return 1;
340   }
341 
342   cerr << "   " << tblHandle << " found.\n";
343 
344   // 4. Test EscapeString.
345   cerr << "@@ Escaping a naughty string...";
346 
347   vtkStdString queryStr = "INSERT INTO atable (somename,somenmbr) VALUES ( " +
348     query->EscapeString(vtkStdString("Str\"ang'eS\ntring"), true) + ", 2 )";
349   query->SetQuery(queryStr);
350   if (!query->Execute())
351   {
352     cerr << "Query failed" << endl;
353     query->Delete();
354     db->Delete();
355     return 1;
356   }
357 
358   cerr << " done." << endl;
359 
360   // 5. Read back the escaped string to verify it worked.
361   cerr << "@@ Reading it back... <";
362 
363   queryStr = "SELECT somename FROM atable WHERE somenmbr=2";
364   query->SetQuery(queryStr);
365   if (!query->Execute())
366   {
367     cerr << "Query failed" << endl;
368     query->Delete();
369     db->Delete();
370     return 1;
371   }
372 
373   if (!query->NextRow())
374   {
375     cerr << "Query returned no results" << endl;
376     query->Delete();
377     db->Delete();
378     return 1;
379   }
380 
381   cerr << query->DataValue(0).ToString().c_str() << "> ";
382   cerr << " done." << endl;
383 
384   // 6. Drop tables
385   cerr << "@@ Dropping these tables...";
386 
387   for (std::vector<vtkStdString>::iterator it = tables.begin(); it != tables.end(); ++it)
388   {
389     queryStr = "DROP TABLE ";
390     queryStr += *it;
391     query->SetQuery(queryStr);
392 
393     if (!query->Execute())
394     {
395       cerr << "Query failed" << endl;
396       return 1;
397     }
398   }
399 
400   cerr << " done." << endl;
401 
402   // Clean up
403   dbSch->Delete();
404   query->Delete();
405 
406   return 0;
407 }
408