diff --git a/storage/src/mozStorageBindingParams.cpp b/storage/src/mozStorageBindingParams.cpp index f1019161a855..cf6298332e57 100644 --- a/storage/src/mozStorageBindingParams.cpp +++ b/storage/src/mozStorageBindingParams.cpp @@ -70,53 +70,58 @@ struct BindingColumnData } // anonymous namespace //////////////////////////////////////////////////////////////////////////////// -//// sqlite3_stmt Specialization Functions (varaintToSQLite3T) +//// Variant Specialization Functions (variantToSQLiteT) -template < > -int +static int sqlite3_T_int(BindingColumnData aData, int aValue) { return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue); } -template < > -int +static int sqlite3_T_int64(BindingColumnData aData, sqlite3_int64 aValue) { return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue); } -template < > -int +static int sqlite3_T_double(BindingColumnData aData, double aValue) { return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue); } -template < > -int +static int +sqlite3_T_text(BindingColumnData aData, + const nsCString& aValue) +{ + return ::sqlite3_bind_text(aData.stmt, + aData.column + 1, + aValue.get(), + aValue.Length(), + SQLITE_TRANSIENT); +} + +static int sqlite3_T_text16(BindingColumnData aData, - nsString aValue) + const nsString& aValue) { return ::sqlite3_bind_text16(aData.stmt, aData.column + 1, - PromiseFlatString(aValue).get(), + aValue.get(), aValue.Length() * 2, // Length in bytes! SQLITE_TRANSIENT); } -template < > -int +static int sqlite3_T_null(BindingColumnData aData) { return ::sqlite3_bind_null(aData.stmt, aData.column + 1); } -template < > -int +static int sqlite3_T_blob(BindingColumnData aData, const void *aBlob, int aSize) @@ -126,6 +131,8 @@ sqlite3_T_blob(BindingColumnData aData, } +#include "variantToSQLiteT_impl.h" + //////////////////////////////////////////////////////////////////////////////// //// BindingParams diff --git a/storage/src/mozStorageConnection.cpp b/storage/src/mozStorageConnection.cpp index 9ac387e56d3a..56c8e19f25bb 100644 --- a/storage/src/mozStorageConnection.cpp +++ b/storage/src/mozStorageConnection.cpp @@ -79,10 +79,9 @@ namespace storage { #define PREF_TS_SYNCHRONOUS "toolkit.storage.synchronous" //////////////////////////////////////////////////////////////////////////////// -//// sqlite3_context Specialization Functions +//// Variant Specialization Functions (variantToSQLiteT) -template < > -int +static int sqlite3_T_int(sqlite3_context *aCtx, int aValue) { @@ -90,8 +89,7 @@ sqlite3_T_int(sqlite3_context *aCtx, return SQLITE_OK; } -template < > -int +static int sqlite3_T_int64(sqlite3_context *aCtx, sqlite3_int64 aValue) { @@ -99,8 +97,7 @@ sqlite3_T_int64(sqlite3_context *aCtx, return SQLITE_OK; } -template < > -int +static int sqlite3_T_double(sqlite3_context *aCtx, double aValue) { @@ -108,28 +105,36 @@ sqlite3_T_double(sqlite3_context *aCtx, return SQLITE_OK; } -template < > -int +static int +sqlite3_T_text(sqlite3_context *aCtx, + const nsCString &aValue) +{ + ::sqlite3_result_text(aCtx, + aValue.get(), + aValue.Length(), + SQLITE_TRANSIENT); + return SQLITE_OK; +} + +static int sqlite3_T_text16(sqlite3_context *aCtx, - nsString aValue) + const nsString &aValue) { ::sqlite3_result_text16(aCtx, - PromiseFlatString(aValue).get(), + aValue.get(), aValue.Length() * 2, // Number of bytes. SQLITE_TRANSIENT); return SQLITE_OK; } -template < > -int +static int sqlite3_T_null(sqlite3_context *aCtx) { ::sqlite3_result_null(aCtx); return SQLITE_OK; } -template < > -int +static int sqlite3_T_blob(sqlite3_context *aCtx, const void *aData, int aSize) @@ -138,6 +143,8 @@ sqlite3_T_blob(sqlite3_context *aCtx, return SQLITE_OK; } +#include "variantToSQLiteT_impl.h" + //////////////////////////////////////////////////////////////////////////////// //// Local Functions diff --git a/storage/src/mozStoragePrivateHelpers.h b/storage/src/mozStoragePrivateHelpers.h index 65f948654e9a..56fac68276ad 100644 --- a/storage/src/mozStoragePrivateHelpers.h +++ b/storage/src/mozStoragePrivateHelpers.h @@ -93,13 +93,6 @@ bindJSValue(JSContext *aCtx, int aIdx, jsval aValue); -/** - * Used to convert an nsIVariant to the proper SQLite type. - */ -template -int variantToSQLiteT(T aObj, nsIVariant *aValue); -#include "variantToSQLiteT_impl.h" // To keep this file easier to read. - } // namespace storage } // namespace mozilla diff --git a/storage/src/variantToSQLiteT_impl.h b/storage/src/variantToSQLiteT_impl.h index 73fd2e1d4320..5ee2d02936f0 100644 --- a/storage/src/variantToSQLiteT_impl.h +++ b/storage/src/variantToSQLiteT_impl.h @@ -40,21 +40,8 @@ // Note: we are already in the namepace mozilla::storage -//////////////////////////////////////////////////////////////////////////////// -//// Specialization Declarations - -template -int sqlite3_T_int(T aObj, int aValue); -template -int sqlite3_T_int64(T aObj, sqlite_int64 aValue); -template -int sqlite3_T_double(T aObj, double aValue); -template -int sqlite3_T_text16(T aObj, nsString aValue); -template -int sqlite3_T_null(T aObj); -template -int sqlite3_T_blob(T aObj, const void *aBlob, int aSize); +// Note 2: whoever #includes this file must provide implementations of +// sqlite3_T_* prior. //////////////////////////////////////////////////////////////////////////////// //// variantToSQLiteT Implementation @@ -108,14 +95,23 @@ variantToSQLiteT(T aObj, return sqlite3_T_int(aObj, value ? 1 : 0); } case nsIDataType::VTYPE_CHAR: - case nsIDataType::VTYPE_WCHAR: - case nsIDataType::VTYPE_DOMSTRING: case nsIDataType::VTYPE_CHAR_STR: - case nsIDataType::VTYPE_WCHAR_STR: case nsIDataType::VTYPE_STRING_SIZE_IS: - case nsIDataType::VTYPE_WSTRING_SIZE_IS: case nsIDataType::VTYPE_UTF8STRING: case nsIDataType::VTYPE_CSTRING: + { + nsCAutoString value; + // GetAsAUTF8String should never perform conversion when coming from + // 8-bit string types, and thus can accept strings with arbitrary encoding + // (including UTF8 and ASCII). + nsresult rv = aValue->GetAsAUTF8String(value); + NS_ENSURE_SUCCESS(rv, SQLITE_MISMATCH); + return sqlite3_T_text(aObj, value); + } + case nsIDataType::VTYPE_WCHAR: + case nsIDataType::VTYPE_DOMSTRING: + case nsIDataType::VTYPE_WCHAR_STR: + case nsIDataType::VTYPE_WSTRING_SIZE_IS: case nsIDataType::VTYPE_ASTRING: { nsAutoString value; diff --git a/storage/test/Makefile.in b/storage/test/Makefile.in index d80706e358d7..c83d41efcf94 100644 --- a/storage/test/Makefile.in +++ b/storage/test/Makefile.in @@ -52,6 +52,7 @@ CPP_UNIT_TESTS = \ test_transaction_helper.cpp \ test_statement_scoper.cpp \ test_mutex.cpp \ + test_binding_params.cpp \ $(NULL) ifdef MOZ_DEBUG diff --git a/storage/test/storage_test_harness.h b/storage/test/storage_test_harness.h index 3de1da345355..2849b3f9c858 100644 --- a/storage/test/storage_test_harness.h +++ b/storage/test/storage_test_harness.h @@ -42,8 +42,8 @@ #include "mozIStorageService.h" #include "mozIStorageConnection.h" -static size_t gTotalTests = 0; -static size_t gPassedTests = 0; +static int gTotalTests = 0; +static int gPassedTests = 0; #define do_check_true(aCondition) \ PR_BEGIN_MACRO \ diff --git a/storage/test/storage_test_harness_tail.h b/storage/test/storage_test_harness_tail.h index 660fc4da08ca..65a7be27ad76 100644 --- a/storage/test/storage_test_harness_tail.h +++ b/storage/test/storage_test_harness_tail.h @@ -57,5 +57,5 @@ main(int aArgc, if (gPassedTests == gTotalTests) passed(TEST_FILE); - (void)printf("%zu of %zu tests passed\n", gPassedTests, gTotalTests); + (void)printf("%i of %i tests passed\n", gPassedTests, gTotalTests); } diff --git a/storage/test/test_binding_params.cpp b/storage/test/test_binding_params.cpp new file mode 100644 index 000000000000..ac7ebaeae438 --- /dev/null +++ b/storage/test/test_binding_params.cpp @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is storage test code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Witte (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "storage_test_harness.h" + +#include "mozStorageHelper.h" + +/** + * This file tests binding and reading out string parameters through the + * mozIStorageStatement API. + */ + +void +test_ASCIIString() +{ + nsCOMPtr db(getMemoryDatabase()); + + // Create table with a single string column. + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE test (str STRING)" + )); + + // Create statements to INSERT and SELECT the string. + nsCOMPtr insert, select; + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (str) VALUES (?1)" + ), getter_AddRefs(insert)); + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "SELECT str FROM test" + ), getter_AddRefs(select)); + + // Roundtrip a string through the table, and ensure it comes out as expected. + nsCAutoString inserted("I'm an ASCII string"); + { + mozStorageStatementScoper scoper(insert); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(insert->BindUTF8StringParameter(0, inserted))); + do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult))); + do_check_false(hasResult); + } + + nsCAutoString result; + { + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result))); + } + + do_check_true(result == inserted); + + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test")); +} + +void +test_CString() +{ + nsCOMPtr db(getMemoryDatabase()); + + // Create table with a single string column. + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE test (str STRING)" + )); + + // Create statements to INSERT and SELECT the string. + nsCOMPtr insert, select; + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (str) VALUES (?1)" + ), getter_AddRefs(insert)); + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "SELECT str FROM test" + ), getter_AddRefs(select)); + + // Roundtrip a string through the table, and ensure it comes out as expected. + static const char sCharArray[] = + "I'm not a \xff\x00\xac\xde\xbb ASCII string!"; + nsCAutoString inserted(sCharArray, NS_ARRAY_LENGTH(sCharArray) - 1); + do_check_true(inserted.Length() == NS_ARRAY_LENGTH(sCharArray) - 1); + { + mozStorageStatementScoper scoper(insert); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(insert->BindUTF8StringParameter(0, inserted))); + do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult))); + do_check_false(hasResult); + } + + { + nsCAutoString result; + + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result))); + + do_check_true(result == inserted); + } + + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test")); +} + +void +test_UTFStrings() +{ + nsCOMPtr db(getMemoryDatabase()); + + // Create table with a single string column. + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE test (str STRING)" + )); + + // Create statements to INSERT and SELECT the string. + nsCOMPtr insert, select; + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO test (str) VALUES (?1)" + ), getter_AddRefs(insert)); + (void)db->CreateStatement(NS_LITERAL_CSTRING( + "SELECT str FROM test" + ), getter_AddRefs(select)); + + // Roundtrip a UTF8 string through the table, using UTF8 input and output. + static const char sCharArray[] = + "I'm a \xc3\xbb\xc3\xbc\xc3\xa2\xc3\xa4\xc3\xa7 UTF8 string!"; + nsCAutoString insertedUTF8(sCharArray, NS_ARRAY_LENGTH(sCharArray) - 1); + do_check_true(insertedUTF8.Length() == NS_ARRAY_LENGTH(sCharArray) - 1); + NS_ConvertUTF8toUTF16 insertedUTF16(insertedUTF8); + do_check_true(insertedUTF8 == NS_ConvertUTF16toUTF8(insertedUTF16)); + { + mozStorageStatementScoper scoper(insert); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(insert->BindUTF8StringParameter(0, insertedUTF8))); + do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult))); + do_check_false(hasResult); + } + + { + nsCAutoString result; + + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result))); + + do_check_true(result == insertedUTF8); + } + + // Use UTF8 input and UTF16 output. + { + nsAutoString result; + + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetString(0, result))); + + do_check_true(result == insertedUTF16); + } + + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test")); + + // Roundtrip the same string using UTF16 input and UTF8 output. + { + mozStorageStatementScoper scoper(insert); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(insert->BindStringParameter(0, insertedUTF16))); + do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult))); + do_check_false(hasResult); + } + + { + nsCAutoString result; + + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result))); + + do_check_true(result == insertedUTF8); + } + + // Use UTF16 input and UTF16 output. + { + nsAutoString result; + + mozStorageStatementScoper scoper(select); + PRBool hasResult; + do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult))); + do_check_true(hasResult); + do_check_true(NS_SUCCEEDED(select->GetString(0, result))); + + do_check_true(result == insertedUTF16); + } + + (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test")); +} + +void (*gTests[])(void) = { + test_ASCIIString, + test_CString, + test_UTFStrings, +}; + +const char *file = __FILE__; +#define TEST_NAME "binding string params" +#define TEST_FILE file +#include "storage_test_harness_tail.h"