1 /* Any copyright is dedicated to the Public Domain.
2    http://creativecommons.org/publicdomain/zero/1.0/ */
3 
4 #include "storage_test_harness.h"
5 
6 #include "nsIEventTarget.h"
7 #include "mozStorageConnection.h"
8 
9 #include "sqlite3.h"
10 
11 using namespace mozilla;
12 using namespace mozilla::storage;
13 
14 ////////////////////////////////////////////////////////////////////////////////
15 //// Helpers
16 
17 /**
18  * Commit hook to detect transactions.
19  *
20  * @param aArg
21  *        An integer pointer that will be incremented for each commit.
22  */
commit_hook(void * aArg)23 int commit_hook(void *aArg)
24 {
25   int *arg = static_cast<int *>(aArg);
26   (*arg)++;
27   return 0;
28 }
29 
30 /**
31  * Executes the passed-in statements and checks if a transaction is created.
32  * When done statements are finalized and database connection is closed.
33  *
34  * @param aDB
35  *        The database connection.
36  * @param aStmts
37  *        Vector of statements.
38  * @param aStmtsLen
39  *        Number of statements.
40  * @param aTransactionExpected
41  *        Whether a transaction is expected or not.
42  */
43 void
check_transaction(mozIStorageConnection * aDB,mozIStorageBaseStatement ** aStmts,uint32_t aStmtsLen,bool aTransactionExpected)44 check_transaction(mozIStorageConnection *aDB,
45                   mozIStorageBaseStatement **aStmts,
46                   uint32_t aStmtsLen,
47                   bool aTransactionExpected)
48 {
49   // -- install a transaction commit hook.
50   int commit = 0;
51   static_cast<Connection *>(aDB)->setCommitHook(commit_hook, &commit);
52 
53   RefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
54   nsCOMPtr<mozIStoragePendingStatement> asyncPend;
55   do_check_success(aDB->ExecuteAsync(aStmts, aStmtsLen, asyncSpin,
56                                      getter_AddRefs(asyncPend)));
57   do_check_true(asyncPend);
58 
59   // -- complete the execution
60   asyncSpin->SpinUntilCompleted();
61 
62   // -- uninstall the transaction commit hook.
63   static_cast<Connection *>(aDB)->setCommitHook(nullptr);
64 
65   // -- check transaction
66   do_check_eq(aTransactionExpected, !!commit);
67 
68   // -- check that only one transaction was created.
69   if (aTransactionExpected) {
70     do_check_eq(1, commit);
71   }
72 
73   // -- cleanup
74   for (uint32_t i = 0; i < aStmtsLen; ++i) {
75     aStmts[i]->Finalize();
76   }
77   blocking_async_close(aDB);
78 }
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 //// Tests
82 
83 /**
84  * Test that executing multiple readonly AsyncStatements doesn't create a
85  * transaction.
86  */
TEST(storage_asyncStatementExecution_transaction,MultipleAsyncReadStatements)87 TEST(storage_asyncStatementExecution_transaction, MultipleAsyncReadStatements)
88 {
89   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
90 
91   // -- create statements and execute them
92   nsCOMPtr<mozIStorageAsyncStatement> stmt1;
93   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
94     "SELECT * FROM sqlite_master"
95   ), getter_AddRefs(stmt1));
96 
97   nsCOMPtr<mozIStorageAsyncStatement> stmt2;
98   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
99     "SELECT * FROM sqlite_master"
100   ), getter_AddRefs(stmt2));
101 
102   mozIStorageBaseStatement *stmts[] = {
103     stmt1,
104     stmt2,
105   };
106 
107   check_transaction(db, stmts, ArrayLength(stmts), false);
108 }
109 
110 /**
111  * Test that executing multiple readonly Statements doesn't create a
112  * transaction.
113  */
TEST(storage_asyncStatementExecution_transaction,MultipleReadStatements)114 TEST(storage_asyncStatementExecution_transaction, MultipleReadStatements)
115 {
116   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
117 
118   // -- create statements and execute them
119   nsCOMPtr<mozIStorageStatement> stmt1;
120   db->CreateStatement(NS_LITERAL_CSTRING(
121     "SELECT * FROM sqlite_master"
122   ), getter_AddRefs(stmt1));
123 
124   nsCOMPtr<mozIStorageStatement> stmt2;
125   db->CreateStatement(NS_LITERAL_CSTRING(
126     "SELECT * FROM sqlite_master"
127   ), getter_AddRefs(stmt2));
128 
129   mozIStorageBaseStatement *stmts[] = {
130     stmt1,
131     stmt2,
132   };
133 
134   check_transaction(db, stmts, ArrayLength(stmts), false);
135 }
136 
137 /**
138  * Test that executing multiple AsyncStatements causing writes creates a
139  * transaction.
140  */
TEST(storage_asyncStatementExecution_transaction,MultipleAsyncReadWriteStatements)141 TEST(storage_asyncStatementExecution_transaction, MultipleAsyncReadWriteStatements)
142 {
143   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
144 
145   // -- create statements and execute them
146   nsCOMPtr<mozIStorageAsyncStatement> stmt1;
147   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
148     "SELECT * FROM sqlite_master"
149   ), getter_AddRefs(stmt1));
150 
151   nsCOMPtr<mozIStorageAsyncStatement> stmt2;
152   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
153     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
154   ), getter_AddRefs(stmt2));
155 
156   mozIStorageBaseStatement *stmts[] = {
157     stmt1,
158     stmt2,
159   };
160 
161   check_transaction(db, stmts, ArrayLength(stmts), true);
162 }
163 
164 /**
165  * Test that executing multiple Statements causing writes creates a transaction.
166  */
TEST(storage_asyncStatementExecution_transaction,MultipleReadWriteStatements)167 TEST(storage_asyncStatementExecution_transaction, MultipleReadWriteStatements)
168 {
169   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
170 
171   // -- create statements and execute them
172   nsCOMPtr<mozIStorageStatement> stmt1;
173   db->CreateStatement(NS_LITERAL_CSTRING(
174     "SELECT * FROM sqlite_master"
175   ), getter_AddRefs(stmt1));
176 
177   nsCOMPtr<mozIStorageStatement> stmt2;
178   db->CreateStatement(NS_LITERAL_CSTRING(
179     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
180   ), getter_AddRefs(stmt2));
181 
182   mozIStorageBaseStatement *stmts[] = {
183     stmt1,
184     stmt2,
185   };
186 
187   check_transaction(db, stmts, ArrayLength(stmts), true);
188 }
189 
190 /**
191  * Test that executing multiple AsyncStatements causing writes creates a
192  * single transaction.
193  */
TEST(storage_asyncStatementExecution_transaction,MultipleAsyncWriteStatements)194 TEST(storage_asyncStatementExecution_transaction, MultipleAsyncWriteStatements)
195 {
196   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
197 
198   // -- create statements and execute them
199   nsCOMPtr<mozIStorageAsyncStatement> stmt1;
200   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
201     "CREATE TABLE test1 (id INTEGER PRIMARY KEY)"
202   ), getter_AddRefs(stmt1));
203 
204   nsCOMPtr<mozIStorageAsyncStatement> stmt2;
205   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
206     "CREATE TABLE test2 (id INTEGER PRIMARY KEY)"
207   ), getter_AddRefs(stmt2));
208 
209   mozIStorageBaseStatement *stmts[] = {
210     stmt1,
211     stmt2,
212   };
213 
214   check_transaction(db, stmts, ArrayLength(stmts), true);
215 }
216 
217 /**
218  * Test that executing multiple Statements causing writes creates a
219  * single transaction.
220  */
TEST(storage_asyncStatementExecution_transaction,MultipleWriteStatements)221 TEST(storage_asyncStatementExecution_transaction, MultipleWriteStatements)
222 {
223   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
224 
225   // -- create statements and execute them
226   nsCOMPtr<mozIStorageStatement> stmt1;
227   db->CreateStatement(NS_LITERAL_CSTRING(
228     "CREATE TABLE test1 (id INTEGER PRIMARY KEY)"
229   ), getter_AddRefs(stmt1));
230 
231   nsCOMPtr<mozIStorageStatement> stmt2;
232   db->CreateStatement(NS_LITERAL_CSTRING(
233     "CREATE TABLE test2 (id INTEGER PRIMARY KEY)"
234   ), getter_AddRefs(stmt2));
235 
236   mozIStorageBaseStatement *stmts[] = {
237     stmt1,
238     stmt2,
239   };
240 
241   check_transaction(db, stmts, ArrayLength(stmts), true);
242 }
243 
244 /**
245  * Test that executing a single read-only AsyncStatement doesn't create a
246  * transaction.
247  */
TEST(storage_asyncStatementExecution_transaction,SingleAsyncReadStatement)248 TEST(storage_asyncStatementExecution_transaction, SingleAsyncReadStatement)
249 {
250   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
251 
252   // -- create statements and execute them
253   nsCOMPtr<mozIStorageAsyncStatement> stmt;
254   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
255     "SELECT * FROM sqlite_master"
256   ), getter_AddRefs(stmt));
257 
258   mozIStorageBaseStatement *stmts[] = {
259     stmt,
260   };
261 
262   check_transaction(db, stmts, ArrayLength(stmts), false);
263 }
264 
265 /**
266  * Test that executing a single read-only Statement doesn't create a
267  * transaction.
268  */
TEST(storage_asyncStatementExecution_transaction,SingleReadStatement)269 TEST(storage_asyncStatementExecution_transaction, SingleReadStatement)
270 {
271   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
272 
273   // -- create statements and execute them
274   nsCOMPtr<mozIStorageStatement> stmt;
275   db->CreateStatement(NS_LITERAL_CSTRING(
276     "SELECT * FROM sqlite_master"
277   ), getter_AddRefs(stmt));
278 
279   mozIStorageBaseStatement *stmts[] = {
280     stmt,
281   };
282 
283   check_transaction(db, stmts, ArrayLength(stmts), false);
284 }
285 
286 /**
287  * Test that executing a single AsyncStatement causing writes creates a
288  * transaction.
289  */
TEST(storage_asyncStatementExecution_transaction,SingleAsyncWriteStatement)290 TEST(storage_asyncStatementExecution_transaction, SingleAsyncWriteStatement)
291 {
292   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
293 
294   // -- create statements and execute them
295   nsCOMPtr<mozIStorageAsyncStatement> stmt;
296   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
297     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
298   ), getter_AddRefs(stmt));
299 
300   mozIStorageBaseStatement *stmts[] = {
301     stmt,
302   };
303 
304   check_transaction(db, stmts, ArrayLength(stmts), true);
305 }
306 
307 /**
308  * Test that executing a single Statement causing writes creates a transaction.
309  */
TEST(storage_asyncStatementExecution_transaction,SingleWriteStatement)310 TEST(storage_asyncStatementExecution_transaction, SingleWriteStatement)
311 {
312   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
313 
314   // -- create statements and execute them
315   nsCOMPtr<mozIStorageStatement> stmt;
316   db->CreateStatement(NS_LITERAL_CSTRING(
317     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
318   ), getter_AddRefs(stmt));
319 
320   mozIStorageBaseStatement *stmts[] = {
321     stmt,
322   };
323 
324   check_transaction(db, stmts, ArrayLength(stmts), true);
325 }
326 
327 /**
328  * Test that executing a single read-only AsyncStatement with multiple params
329  * doesn't create a transaction.
330  */
TEST(storage_asyncStatementExecution_transaction,MultipleParamsAsyncReadStatement)331 TEST(storage_asyncStatementExecution_transaction, MultipleParamsAsyncReadStatement)
332 {
333   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
334 
335   // -- create statements and execute them
336   nsCOMPtr<mozIStorageAsyncStatement> stmt;
337   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
338     "SELECT :param FROM sqlite_master"
339   ), getter_AddRefs(stmt));
340 
341   // -- bind multiple BindingParams
342   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
343   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
344   for (int32_t i = 0; i < 2; i++) {
345     nsCOMPtr<mozIStorageBindingParams> params;
346     paramsArray->NewBindingParams(getter_AddRefs(params));
347     params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
348     paramsArray->AddParams(params);
349   }
350   stmt->BindParameters(paramsArray);
351   paramsArray = nullptr;
352 
353   mozIStorageBaseStatement *stmts[] = {
354     stmt,
355   };
356 
357   check_transaction(db, stmts, ArrayLength(stmts), false);
358 }
359 
360 /**
361  * Test that executing a single read-only Statement with multiple params
362  * doesn't create a transaction.
363  */
TEST(storage_asyncStatementExecution_transaction,MultipleParamsReadStatement)364 TEST(storage_asyncStatementExecution_transaction, MultipleParamsReadStatement)
365 {
366   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
367 
368   // -- create statements and execute them
369   nsCOMPtr<mozIStorageStatement> stmt;
370   db->CreateStatement(NS_LITERAL_CSTRING(
371     "SELECT :param FROM sqlite_master"
372   ), getter_AddRefs(stmt));
373 
374   // -- bind multiple BindingParams
375   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
376   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
377   for (int32_t i = 0; i < 2; i++) {
378     nsCOMPtr<mozIStorageBindingParams> params;
379     paramsArray->NewBindingParams(getter_AddRefs(params));
380     params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
381     paramsArray->AddParams(params);
382   }
383   stmt->BindParameters(paramsArray);
384   paramsArray = nullptr;
385 
386   mozIStorageBaseStatement *stmts[] = {
387     stmt,
388   };
389 
390   check_transaction(db, stmts, ArrayLength(stmts), false);
391 }
392 
393 /**
394  * Test that executing a single write AsyncStatement with multiple params
395  * creates a transaction.
396  */
TEST(storage_asyncStatementExecution_transaction,MultipleParamsAsyncWriteStatement)397 TEST(storage_asyncStatementExecution_transaction, MultipleParamsAsyncWriteStatement)
398 {
399   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
400 
401   // -- create a table for writes
402   nsCOMPtr<mozIStorageStatement> tableStmt;
403   db->CreateStatement(NS_LITERAL_CSTRING(
404     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
405   ), getter_AddRefs(tableStmt));
406   tableStmt->Execute();
407   tableStmt->Finalize();
408 
409   // -- create statements and execute them
410   nsCOMPtr<mozIStorageAsyncStatement> stmt;
411   db->CreateAsyncStatement(NS_LITERAL_CSTRING(
412     "DELETE FROM test WHERE id = :param"
413   ), getter_AddRefs(stmt));
414 
415   // -- bind multiple BindingParams
416   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
417   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
418   for (int32_t i = 0; i < 2; i++) {
419     nsCOMPtr<mozIStorageBindingParams> params;
420     paramsArray->NewBindingParams(getter_AddRefs(params));
421     params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
422     paramsArray->AddParams(params);
423   }
424   stmt->BindParameters(paramsArray);
425   paramsArray = nullptr;
426 
427   mozIStorageBaseStatement *stmts[] = {
428     stmt,
429   };
430 
431   check_transaction(db, stmts, ArrayLength(stmts), true);
432 }
433 
434 /**
435  * Test that executing a single write Statement with multiple params
436  * creates a transaction.
437  */
TEST(storage_asyncStatementExecution_transaction,MultipleParamsWriteStatement)438 TEST(storage_asyncStatementExecution_transaction, MultipleParamsWriteStatement)
439 {
440   nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
441 
442   // -- create a table for writes
443   nsCOMPtr<mozIStorageStatement> tableStmt;
444   db->CreateStatement(NS_LITERAL_CSTRING(
445     "CREATE TABLE test (id INTEGER PRIMARY KEY)"
446   ), getter_AddRefs(tableStmt));
447   tableStmt->Execute();
448   tableStmt->Finalize();
449 
450   // -- create statements and execute them
451   nsCOMPtr<mozIStorageStatement> stmt;
452   db->CreateStatement(NS_LITERAL_CSTRING(
453     "DELETE FROM test WHERE id = :param"
454   ), getter_AddRefs(stmt));
455 
456   // -- bind multiple BindingParams
457   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
458   stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
459   for (int32_t i = 0; i < 2; i++) {
460     nsCOMPtr<mozIStorageBindingParams> params;
461     paramsArray->NewBindingParams(getter_AddRefs(params));
462     params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1);
463     paramsArray->AddParams(params);
464   }
465   stmt->BindParameters(paramsArray);
466   paramsArray = nullptr;
467 
468   mozIStorageBaseStatement *stmts[] = {
469     stmt,
470   };
471 
472   check_transaction(db, stmts, ArrayLength(stmts), true);
473 }
474