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