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