michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: #include "storage_test_harness.h" michael@0: michael@0: #include "nsIEventTarget.h" michael@0: #include "mozStorageConnection.h" michael@0: michael@0: #include "sqlite3.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::storage; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Helpers michael@0: michael@0: /** michael@0: * Commit hook to detect transactions. michael@0: * michael@0: * @param aArg michael@0: * An integer pointer that will be incremented for each commit. michael@0: */ michael@0: int commit_hook(void *aArg) michael@0: { michael@0: int *arg = static_cast(aArg); michael@0: (*arg)++; michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Executes the passed-in statements and checks if a transaction is created. michael@0: * When done statements are finalized and database connection is closed. michael@0: * michael@0: * @param aDB michael@0: * The database connection. michael@0: * @param aStmts michael@0: * Vector of statements. michael@0: * @param aStmtsLen michael@0: * Number of statements. michael@0: * @param aTransactionExpected michael@0: * Whether a transaction is expected or not. michael@0: */ michael@0: void michael@0: check_transaction(mozIStorageConnection *aDB, michael@0: mozIStorageBaseStatement **aStmts, michael@0: uint32_t aStmtsLen, michael@0: bool aTransactionExpected) michael@0: { michael@0: // -- install a transaction commit hook. michael@0: int commit = 0; michael@0: static_cast(aDB)->setCommitHook(commit_hook, &commit); michael@0: michael@0: nsRefPtr asyncSpin(new AsyncStatementSpinner()); michael@0: nsCOMPtr asyncPend; michael@0: do_check_success(aDB->ExecuteAsync(aStmts, aStmtsLen, asyncSpin, michael@0: getter_AddRefs(asyncPend))); michael@0: do_check_true(asyncPend); michael@0: michael@0: // -- complete the execution michael@0: asyncSpin->SpinUntilCompleted(); michael@0: michael@0: // -- uninstall the transaction commit hook. michael@0: static_cast(aDB)->setCommitHook(nullptr); michael@0: michael@0: // -- check transaction michael@0: do_check_eq(aTransactionExpected, !!commit); michael@0: michael@0: // -- check that only one transaction was created. michael@0: if (aTransactionExpected) { michael@0: do_check_eq(1, commit); michael@0: } michael@0: michael@0: // -- cleanup michael@0: for (uint32_t i = 0; i < aStmtsLen; ++i) { michael@0: aStmts[i]->Finalize(); michael@0: } michael@0: blocking_async_close(aDB); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Tests michael@0: michael@0: /** michael@0: * Test that executing multiple readonly AsyncStatements doesn't create a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_MultipleAsyncReadStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing multiple readonly Statements doesn't create a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_MultipleReadStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing multiple AsyncStatements causing writes creates a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_MultipleAsyncReadWriteStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing multiple Statements causing writes creates a transaction. michael@0: */ michael@0: void michael@0: test_MultipleReadWriteStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing multiple AsyncStatements causing writes creates a michael@0: * single transaction. michael@0: */ michael@0: void michael@0: test_MultipleAsyncWriteStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test1 (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test2 (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing multiple Statements causing writes creates a michael@0: * single transaction. michael@0: */ michael@0: void michael@0: test_MultipleWriteStatements() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt1; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test1 (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt1)); michael@0: michael@0: nsCOMPtr stmt2; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test2 (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt2)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt1, michael@0: stmt2, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single read-only AsyncStatement doesn't create a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_SingleAsyncReadStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single read-only Statement doesn't create a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_SingleReadStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT * FROM sqlite_master" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single AsyncStatement causing writes creates a michael@0: * transaction. michael@0: */ michael@0: void michael@0: test_SingleAsyncWriteStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single Statement causing writes creates a transaction. michael@0: */ michael@0: void michael@0: test_SingleWriteStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single read-only AsyncStatement with multiple params michael@0: * doesn't create a transaction. michael@0: */ michael@0: void michael@0: test_MultipleParamsAsyncReadStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "SELECT :param FROM sqlite_master" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: // -- bind multiple BindingParams michael@0: nsCOMPtr paramsArray; michael@0: stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); michael@0: for (int32_t i = 0; i < 2; i++) { michael@0: nsCOMPtr params; michael@0: paramsArray->NewBindingParams(getter_AddRefs(params)); michael@0: params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1); michael@0: paramsArray->AddParams(params); michael@0: } michael@0: stmt->BindParameters(paramsArray); michael@0: paramsArray = nullptr; michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single read-only Statement with multiple params michael@0: * doesn't create a transaction. michael@0: */ michael@0: void michael@0: test_MultipleParamsReadStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "SELECT :param FROM sqlite_master" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: // -- bind multiple BindingParams michael@0: nsCOMPtr paramsArray; michael@0: stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); michael@0: for (int32_t i = 0; i < 2; i++) { michael@0: nsCOMPtr params; michael@0: paramsArray->NewBindingParams(getter_AddRefs(params)); michael@0: params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1); michael@0: paramsArray->AddParams(params); michael@0: } michael@0: stmt->BindParameters(paramsArray); michael@0: paramsArray = nullptr; michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), false); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single write AsyncStatement with multiple params michael@0: * creates a transaction. michael@0: */ michael@0: void michael@0: test_MultipleParamsAsyncWriteStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create a table for writes michael@0: nsCOMPtr tableStmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(tableStmt)); michael@0: tableStmt->Execute(); michael@0: tableStmt->Finalize(); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateAsyncStatement(NS_LITERAL_CSTRING( michael@0: "DELETE FROM test WHERE id = :param" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: // -- bind multiple BindingParams michael@0: nsCOMPtr paramsArray; michael@0: stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); michael@0: for (int32_t i = 0; i < 2; i++) { michael@0: nsCOMPtr params; michael@0: paramsArray->NewBindingParams(getter_AddRefs(params)); michael@0: params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1); michael@0: paramsArray->AddParams(params); michael@0: } michael@0: stmt->BindParameters(paramsArray); michael@0: paramsArray = nullptr; michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: /** michael@0: * Test that executing a single write Statement with multiple params michael@0: * creates a transaction. michael@0: */ michael@0: void michael@0: test_MultipleParamsWriteStatement() michael@0: { michael@0: nsCOMPtr db(getMemoryDatabase()); michael@0: michael@0: // -- create a table for writes michael@0: nsCOMPtr tableStmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "CREATE TABLE test (id INTEGER PRIMARY KEY)" michael@0: ), getter_AddRefs(tableStmt)); michael@0: tableStmt->Execute(); michael@0: tableStmt->Finalize(); michael@0: michael@0: // -- create statements and execute them michael@0: nsCOMPtr stmt; michael@0: db->CreateStatement(NS_LITERAL_CSTRING( michael@0: "DELETE FROM test WHERE id = :param" michael@0: ), getter_AddRefs(stmt)); michael@0: michael@0: // -- bind multiple BindingParams michael@0: nsCOMPtr paramsArray; michael@0: stmt->NewBindingParamsArray(getter_AddRefs(paramsArray)); michael@0: for (int32_t i = 0; i < 2; i++) { michael@0: nsCOMPtr params; michael@0: paramsArray->NewBindingParams(getter_AddRefs(params)); michael@0: params->BindInt32ByName(NS_LITERAL_CSTRING("param"), 1); michael@0: paramsArray->AddParams(params); michael@0: } michael@0: stmt->BindParameters(paramsArray); michael@0: paramsArray = nullptr; michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: stmt, michael@0: }; michael@0: michael@0: check_transaction(db, stmts, ArrayLength(stmts), true); michael@0: } michael@0: michael@0: void (*gTests[])(void) = { michael@0: test_MultipleAsyncReadStatements, michael@0: test_MultipleReadStatements, michael@0: test_MultipleAsyncReadWriteStatements, michael@0: test_MultipleReadWriteStatements, michael@0: test_MultipleAsyncWriteStatements, michael@0: test_MultipleWriteStatements, michael@0: test_SingleAsyncReadStatement, michael@0: test_SingleReadStatement, michael@0: test_SingleAsyncWriteStatement, michael@0: test_SingleWriteStatement, michael@0: test_MultipleParamsAsyncReadStatement, michael@0: test_MultipleParamsReadStatement, michael@0: test_MultipleParamsAsyncWriteStatement, michael@0: test_MultipleParamsWriteStatement, michael@0: }; michael@0: michael@0: const char *file = __FILE__; michael@0: #define TEST_NAME "async statement execution transaction" michael@0: #define TEST_FILE file michael@0: #include "storage_test_harness_tail.h"