Bug 499990 - Locale-aware collation, r=sdwilsh, sr=vladimir

This commit is contained in:
Drew Willcoxon 2009-07-15 10:49:05 -07:00
parent d1ebeafde8
commit 1dbee03d3f
11 changed files with 1204 additions and 21 deletions

View File

@ -85,6 +85,7 @@ CPPSRCS = \
mozStoragePrivateHelpers.cpp \
mozStorageBindingParamsArray.cpp \
mozStorageBindingParams.cpp \
SQLCollations.cpp \
$(NULL)
LOCAL_INCLUDES = \

View File

@ -0,0 +1,269 @@
/* -*- 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 Mozilla Storage code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Drew Willcoxon <adw@mozilla.com> (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 "SQLCollations.h"
namespace mozilla {
namespace storage {
////////////////////////////////////////////////////////////////////////////////
//// Local Helper Functions
namespace {
/**
* Helper function for the UTF-8 locale collations.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes in aStr1.
* @param aStr1
* The string to be compared against aStr2 as provided by SQLite. It
* must be a non-null-terminated char* buffer.
* @param aLen2
* The number of bytes in aStr2.
* @param aStr2
* The string to be compared against aStr1 as provided by SQLite. It
* must be a non-null-terminated char* buffer.
* @param aComparisonStrength
* The sorting strength, one of the nsICollation constants.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
int
localeCollationHelper8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2,
PRInt32 aComparisonStrength)
{
NS_ConvertUTF8toUTF16 str1(static_cast<const char *>(aStr1), aLen1);
NS_ConvertUTF8toUTF16 str2(static_cast<const char *>(aStr2), aLen2);
Service *serv = static_cast<Service *>(aService);
return serv->localeCompareStrings(str1, str2, aComparisonStrength);
}
/**
* Helper function for the UTF-16 locale collations.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes (not characters) in aStr1.
* @param aStr1
* The string to be compared against aStr2 as provided by SQLite. It
* must be a non-null-terminated PRUnichar* buffer.
* @param aLen2
* The number of bytes (not characters) in aStr2.
* @param aStr2
* The string to be compared against aStr1 as provided by SQLite. It
* must be a non-null-terminated PRUnichar* buffer.
* @param aComparisonStrength
* The sorting strength, one of the nsICollation constants.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
int
localeCollationHelper16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2,
PRInt32 aComparisonStrength)
{
const PRUnichar *buf1 = static_cast<const PRUnichar *>(aStr1);
const PRUnichar *buf2 = static_cast<const PRUnichar *>(aStr2);
// The second argument to the nsDependentSubstring constructor is exclusive:
// It points to the PRUnichar immediately following the last one in the target
// substring. Since aLen1 and aLen2 are in bytes, divide by sizeof(PRUnichar)
// so that the pointer arithmetic is correct.
nsDependentSubstring str1(buf1, buf1 + (aLen1 / sizeof(PRUnichar)));
nsDependentSubstring str2(buf2, buf2 + (aLen2 / sizeof(PRUnichar)));
Service *serv = static_cast<Service *>(aService);
return serv->localeCompareStrings(str1, str2, aComparisonStrength);
}
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
//// Exposed Functions
int
registerCollations(sqlite3 *aDB,
Service *aService)
{
struct Collations {
const char *zName;
int enc;
int(*xCompare)(void*, int, const void*, int, const void*);
} collations[] = {
{"locale",
SQLITE_UTF8,
localeCollation8},
{"locale_case_sensitive",
SQLITE_UTF8,
localeCollationCaseSensitive8},
{"locale_accent_sensitive",
SQLITE_UTF8,
localeCollationAccentSensitive8},
{"locale_case_accent_sensitive",
SQLITE_UTF8,
localeCollationCaseAccentSensitive8},
{"locale",
SQLITE_UTF16,
localeCollation16},
{"locale_case_sensitive",
SQLITE_UTF16,
localeCollationCaseSensitive16},
{"locale_accent_sensitive",
SQLITE_UTF16,
localeCollationAccentSensitive16},
{"locale_case_accent_sensitive",
SQLITE_UTF16,
localeCollationCaseAccentSensitive16},
};
int rv = SQLITE_OK;
for (size_t i = 0; SQLITE_OK == rv && i < NS_ARRAY_LENGTH(collations); ++i) {
struct Collations *p = &collations[i];
rv = ::sqlite3_create_collation(aDB, p->zName, p->enc, aService,
p->xCompare);
}
return rv;
}
////////////////////////////////////////////////////////////////////////////////
//// SQL Collations
int
localeCollation8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInSensitive);
}
int
localeCollationCaseSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationAccentInsenstive);
}
int
localeCollationAccentSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInsensitiveAscii);
}
int
localeCollationCaseAccentSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseSensitive);
}
int
localeCollation16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInSensitive);
}
int
localeCollationCaseSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationAccentInsenstive);
}
int
localeCollationAccentSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInsensitiveAscii);
}
int
localeCollationCaseAccentSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2)
{
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseSensitive);
}
} // namespace storage
} // namespace mozilla

282
storage/src/SQLCollations.h Normal file
View File

@ -0,0 +1,282 @@
/* -*- 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 Mozilla Storage code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Drew Willcoxon <adw@mozilla.com> (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 ***** */
#ifndef _mozilla_storage_SQLCollations_h_
#define _mozilla_storage_SQLCollations_h_
#include "mozStorageService.h"
#include "nscore.h"
#include "nsString.h"
#include "sqlite3.h"
namespace mozilla {
namespace storage {
/**
* Registers the collating sequences declared here with the specified
* database and Service.
*
* @param aDB
* The database we'll be registering the collations with.
* @param aService
* The Service that owns the nsICollation used by our collations.
* @return the SQLite status code indicating success or failure.
*/
NS_HIDDEN_(int) registerCollations(sqlite3 *aDB, Service *aService);
////////////////////////////////////////////////////////////////////////////////
//// Predefined Functions
/**
* Custom UTF-8 collating sequence that respects the application's locale.
* Comparison is case- and accent-insensitive. This is called by SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @param aLen2
* The number of bytes in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollation8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-8 collating sequence that respects the application's locale.
* Comparison is case-sensitive and accent-insensitive. This is called by
* SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @param aLen2
* The number of bytes in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationCaseSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-8 collating sequence that respects the application's locale.
* Comparison is case-insensitive and accent-sensitive. This is called by
* SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @param aLen2
* The number of bytes in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationAccentSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-8 collating sequence that respects the application's locale.
* Comparison is case- and accent-sensitive. This is called by SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @param aLen2
* The number of bytes in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated char* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationCaseAccentSensitive8(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-16 collating sequence that respects the application's locale.
* Comparison is case- and accent-insensitive. This is called by SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes (not characters) in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @param aLen2
* The number of bytes (not characters) in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollation16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-16 collating sequence that respects the application's locale.
* Comparison is case-sensitive and accent-insensitive. This is called by
* SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes (not characters) in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @param aLen2
* The number of bytes (not characters) in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationCaseSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-16 collating sequence that respects the application's locale.
* Comparison is case-insensitive and accent-sensitive. This is called by
* SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes (not characters) in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @param aLen2
* The number of bytes (not characters) in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationAccentSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
/**
* Custom UTF-16 collating sequence that respects the application's locale.
* Comparison is case- and accent-sensitive. This is called by SQLite.
*
* @param aService
* The Service that owns the nsICollation used by this collation.
* @param aLen1
* The number of bytes (not characters) in aStr1.
* @param aStr1
* The string to be compared against aStr2. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @param aLen2
* The number of bytes (not characters) in aStr2.
* @param aStr2
* The string to be compared against aStr1. It will be passed in by
* SQLite as a non-null-terminated PRUnichar* buffer.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0.
*/
NS_HIDDEN_(int) localeCollationCaseAccentSensitive16(void *aService,
int aLen1,
const void *aStr1,
int aLen2,
const void *aStr2);
} // namespace storage
} // namespace mozilla
#endif // _mozilla_storage_SQLCollations_h_

View File

@ -25,6 +25,7 @@
* Brett Wilson <brettw@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
* Lev Serebryakov <lev@serebryakov.spb.ru>
* Drew Willcoxon <adw@mozilla.com>
*
* 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
@ -63,6 +64,7 @@
#include "mozStorageArgValueArray.h"
#include "mozStoragePrivateHelpers.h"
#include "mozStorageStatementData.h"
#include "SQLCollations.h"
#include "prlog.h"
#include "prprf.h"
@ -242,7 +244,7 @@ aggregateFunctionFinalHelper(sqlite3_context *aCtx)
////////////////////////////////////////////////////////////////////////////////
//// Connection
Connection::Connection(mozIStorageService *aService)
Connection::Connection(Service *aService)
: sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
, mDBConn(nsnull)
, mAsyncExecutionMutex(nsAutoLock::NewLock("AsyncExecutionMutex"))
@ -338,7 +340,17 @@ Connection::initialize(nsIFile *aDatabaseFile)
#endif
// Register our built-in SQL functions.
if (registerFunctions(mDBConn) != SQLITE_OK) {
srv = registerFunctions(mDBConn);
if (srv != SQLITE_OK) {
::sqlite3_close(mDBConn);
mDBConn = nsnull;
return convertResultCode(srv);
}
// Register our built-in SQL collating sequences.
srv = registerCollations(mDBConn, mStorageService);
if (srv != SQLITE_OK) {
::sqlite3_close(mDBConn);
mDBConn = nsnull;
return convertResultCode(srv);
}

View File

@ -41,6 +41,7 @@
#ifndef _MOZSTORAGECONNECTION_H_
#define _MOZSTORAGECONNECTION_H_
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "mozilla/Mutex.h"
@ -48,6 +49,7 @@
#include "nsInterfaceHashtable.h"
#include "mozIStorageProgressHandler.h"
#include "mozIStorageConnection.h"
#include "mozStorageService.h"
#include "nsIMutableArray.h"
@ -57,7 +59,6 @@ struct PRLock;
class nsIFile;
class nsIEventTarget;
class nsIThread;
class mozIStorageService;
namespace mozilla {
namespace storage {
@ -68,7 +69,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGECONNECTION
Connection(mozIStorageService* aService);
Connection(Service *aService);
/**
* Creates the connection to the database.
@ -166,9 +167,10 @@ private:
PRLock *mProgressHandlerMutex;
nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;
// This isn't accessed but is used to make sure that the connections do
// not outlive the service.
nsCOMPtr<mozIStorageService> mStorageService;
// This is here for two reasons: 1) It's used to make sure that the
// connections do not outlive the service. 2) Our custom collating functions
// call its localeCompareStrings() method.
nsRefPtr<Service> mStorageService;
};
} // namespace storage

View File

@ -176,7 +176,7 @@ registerFunctions(sqlite3 *aDB)
};
int rv = SQLITE_OK;
for (unsigned i = 0; SQLITE_OK == rv && i < NS_ARRAY_LENGTH(functions); ++i) {
for (size_t i = 0; SQLITE_OK == rv && i < NS_ARRAY_LENGTH(functions); ++i) {
struct Functions *p = &functions[i];
rv = ::sqlite3_create_function(aDB, p->zName, p->nArg, p->enc, p->pContext,
p->xFunc, NULL, NULL);

View File

@ -38,7 +38,7 @@
* ***** END LICENSE BLOCK ***** */
#ifndef _mozStorageSQLFunctions_h_
#define _mozStorageUnicodeFunctions_h_
#define _mozStorageSQLFunctions_h_
#include "sqlite3.h"
#include "nscore.h"

View File

@ -24,6 +24,7 @@
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
* Brett Wilson <brettw@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
* Drew Willcoxon <adw@mozilla.com>
*
* 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
@ -42,10 +43,12 @@
#include "mozStorageService.h"
#include "mozStorageConnection.h"
#include "prinit.h"
#include "nsAutoLock.h"
#include "nsAutoPtr.h"
#include "nsCollationCID.h"
#include "nsEmbedCID.h"
#include "mozStoragePrivateHelpers.h"
#include "nsILocale.h"
#include "nsILocaleService.h"
#include "nsIXPConnect.h"
#include "nsIObserverService.h"
@ -118,6 +121,11 @@ Service::getXPConnect()
return xpc.forget();
}
Service::Service()
: mMutex("Service::mMutex")
{
}
Service::~Service()
{
// Shutdown the sqlite3 API. Warn if shutdown did not turn out okay, but
@ -127,7 +135,6 @@ Service::~Service()
NS_WARNING("sqlite3 did not shutdown cleanly.");
gService = nsnull;
::PR_DestroyLock(mLock);
}
void
@ -139,9 +146,6 @@ Service::shutdown()
nsresult
Service::initialize()
{
mLock = ::PR_NewLock();
NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
// Disable memory allocation statistic collection, improving performance.
// This must be done prior to a call to sqlite3_initialize to have any
// effect.
@ -157,7 +161,7 @@ Service::initialize()
return convertResultCode(rc);
// This makes multiple connections to the same database share the same pager
// cache. We do not need to lock here with mLock because this function is
// cache. We do not need to lock here with mMutex because this function is
// only ever called from Service::GetSingleton, which will only
// call this function once, and will not return until this function returns.
// (It does not matter where this is called relative to sqlite3_initialize.)
@ -177,6 +181,69 @@ Service::initialize()
return NS_OK;
}
int
Service::localeCompareStrings(const nsAString &aStr1,
const nsAString &aStr2,
PRInt32 aComparisonStrength)
{
// The implementation of nsICollation.CompareString() is platform-dependent.
// On Linux it's not thread-safe. It may not be on Windows and OS X either,
// but it's more difficult to tell. We therefore synchronize this method.
MutexAutoLock mutex(mMutex);
nsICollation *coll = getLocaleCollation();
if (!coll) {
NS_ERROR("Storage service has no collation");
return 0;
}
PRInt32 res;
nsresult rv = coll->CompareString(aComparisonStrength, aStr1, aStr2, &res);
if (NS_FAILED(rv)) {
NS_ERROR("Collation compare string failed");
return 0;
}
return res;
}
nsICollation *
Service::getLocaleCollation()
{
mMutex.AssertCurrentThreadOwns();
if (mLocaleCollation)
return mLocaleCollation;
nsCOMPtr<nsILocaleService> svc(do_GetService(NS_LOCALESERVICE_CONTRACTID));
if (!svc) {
NS_WARNING("Could not get locale service");
return nsnull;
}
nsCOMPtr<nsILocale> appLocale;
nsresult rv = svc->GetApplicationLocale(getter_AddRefs(appLocale));
if (NS_FAILED(rv)) {
NS_WARNING("Could not get application locale");
return nsnull;
}
nsCOMPtr<nsICollationFactory> collFact =
do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
if (!collFact) {
NS_WARNING("Could not create collation factory");
return nsnull;
}
rv = collFact->CreateCollation(appLocale, getter_AddRefs(mLocaleCollation));
if (NS_FAILED(rv)) {
NS_WARNING("Could not create collation");
return nsnull;
}
return mLocaleCollation;
}
////////////////////////////////////////////////////////////////////////////////
//// mozIStorageService
@ -228,7 +295,7 @@ Service::OpenDatabase(nsIFile *aDatabaseFile,
NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
{
nsAutoLock lock(mLock);
MutexAutoLock mutex(mMutex);
nsresult rv = msc->initialize(aDatabaseFile);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -252,7 +319,7 @@ Service::OpenUnsharedDatabase(nsIFile *aDatabaseFile,
// without affecting the caches currently in use by other connections.
nsresult rv;
{
nsAutoLock lock(mLock);
MutexAutoLock mutex(mMutex);
int rc = ::sqlite3_enable_shared_cache(0);
if (rc != SQLITE_OK)
return convertResultCode(rc);

View File

@ -24,6 +24,7 @@
* Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
* Brett Wilson <brettw@gmail.com>
* Shawn Wilsher <me@shawnwilsher.com>
* Drew Willcoxon <adw@mozilla.com>
*
* 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
@ -43,9 +44,10 @@
#define _MOZSTORAGESERVICE_H_
#include "nsCOMPtr.h"
#include "nsICollation.h"
#include "nsIFile.h"
#include "nsIObserver.h"
#include "prlock.h"
#include "mozilla/Mutex.h"
#include "mozIStorageService.h"
@ -63,6 +65,23 @@ public:
*/
nsresult initialize();
/**
* Compares two strings using the Service's locale-aware collation.
*
* @param aStr1
* The string to be compared against aStr2.
* @param aStr2
* The string to be compared against aStr1.
* @param aComparisonStrength
* The sorting strength, one of the nsICollation constants.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative
* number. If aStr1 > aStr2, returns a positive number. If
* aStr1 == aStr2, returns 0.
*/
int localeCompareStrings(const nsAString &aStr1,
const nsAString &aStr2,
PRInt32 aComparisonStrength);
static Service *getSingleton();
NS_DECL_ISUPPORTS
@ -76,19 +95,39 @@ public:
static already_AddRefed<nsIXPConnect> getXPConnect();
private:
Service();
virtual ~Service();
/**
* Used for locking around calls when initializing connections so that we
* can ensure that the state of sqlite3_enable_shared_cache is sane.
* Used for 1) locking around calls when initializing connections so that we
* can ensure that the state of sqlite3_enable_shared_cache is sane and 2)
* synchronizing access to mLocaleCollation.
*/
PRLock *mLock;
Mutex mMutex;
/**
* Shuts down the storage service, freeing all of the acquired resources.
*/
void shutdown();
/**
* Lazily creates and returns a collation created from the application's
* locale that all statements of all Connections of this Service may use.
* Since the collation's lifetime is that of the Service and no statement may
* execute outside the lifetime of the Service, this method returns a raw
* pointer.
*/
nsICollation *getLocaleCollation();
/**
* Lazily created collation that all statements of all Connections of this
* Service may use. The collation is created from the application's locale.
*
* @note Collation implementations are platform-dependent and in general not
* thread-safe. Access to this collation should be synchronized.
*/
nsCOMPtr<nsICollation> mLocaleCollation;
nsCOMPtr<nsIFile> mProfileStorageFile;
static Service *gService;

View File

@ -0,0 +1,174 @@
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
ludwig van beethoven
Ludwig van Beethoven
Ludwig van beethoven
Jane
jane
JANE
jAne
jaNe
janE
JAne
JaNe
JanE
JANe
JaNE
JAnE
jANE
Umberto Eco
Umberto eco
umberto eco
umberto Eco
UMBERTO ECO
ace
bash
*ace
!ace
%ace
~ace
#ace
cork
denizen
[denizen]
(denizen)
{denizen}
/denizen/
#denizen#
$denizen$
@denizen
elf
full
gnome
gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Japanese
gnomic investigation of typological factors in the grammaticalization process of Malayo-Polynesian substaratum in the protoAltaic vocabulary core in the proto-layers of pre-historic Javanese
hint
Internationalization
Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization
Zinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizatio
n
Zinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalization internationalizationinternationalizationinternationalizationinternationalizationinternationalization
ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTioninternationalizationinternationalizationinternationalizationinternationalization
ZinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizationinternationalizaTion
jostle
kin
Laymen
lumens
Lumens
motleycrew
motley crew
niven's creative talents
nivens creative talents
opie
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rockies
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the Rokkies
posh restaurants in surbanized and still urban incorporated subsection of this beautifl city in the rockies
quilt's
quilts
quilt
Rondo
street
tamale oxidization and iodization in rapid progress
tamale oxidization and iodization in rapid Progress
until
vera
Wobble
Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoneme
Xanadu's legenary imaginary floccinaucinihilipilification in localization of theoretical portions of glottochronological understanding of the phoname
yearn
zodiac
a
å

View File

@ -0,0 +1,337 @@
/* -*- 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 Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Drew Willcoxon <adw@mozilla.com> (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 ***** */
/**
* Bug 499990 - Locale-aware collation
*
* Tests our custom, locale-aware collating sequences.
*/
// The name of the file containing the strings we'll sort during this test.
// The file's data is taken from intl/locale/tests/sort/us-ascii_base.txt and
// and intl/locale/tests/sort/us-ascii_sort.txt.
const DATA_BASENAME = "locale_collation.txt";
// The test data from DATA_BASENAME is read into this array.
var gStrings;
// A collation created from the application's locale. Used by localeCompare().
var gLocaleCollation;
// A connection to our in-memory UTF-16-encoded database.
var gUtf16Conn;
///////////////////////////////////////////////////////////////////////////////
//// Helper Functions
/**
* Since we create a UTF-16 database we have to clean it up, in addition to
* the normal cleanup of Storage tests.
*/
function cleanupLocaleTests()
{
print("-- Cleaning up test_locale_collation.js suite.");
gUtf16Conn.close();
cleanup();
}
/**
* Creates a test database similar to the default one created in
* head_storage.js, except that this one uses UTF-16 encoding.
*
* @return A connection to the database.
*/
function createUtf16Database()
{
print("Creating the in-memory UTF-16-encoded database.");
let conn = getService().openSpecialDatabase("memory");
conn.executeSimpleSQL("PRAGMA encoding = 'UTF-16'");
print("Make sure the encoding was set correctly and is now UTF-16.");
let stmt = conn.createStatement("PRAGMA encoding");
do_check_true(stmt.executeStep());
let enc = stmt.getString(0);
stmt.finalize();
// The value returned will actually be UTF-16le or UTF-16be.
do_check_true(enc === "UTF-16le" || enc === "UTF-16be");
return conn;
}
/**
* Compares aActual to aExpected, ensuring that the numbers and orderings of
* the two arrays' elements are the same.
*
* @param aActual
* An array of strings retrieved from the database.
* @param aExpected
* An array of strings to which aActual should be equivalent.
*/
function ensureResultsAreCorrect(aActual, aExpected)
{
print("Actual results: " + aActual);
print("Expected results: " + aExpected);
do_check_eq(aActual.length, aExpected.length);
for (let i = 0; i < aActual.length; i++)
do_check_eq(aActual[i], aExpected[i]);
}
/**
* Synchronously SELECTs all rows from the test table of the given database
* using the given collation.
*
* @param aCollation
* The name of one of our custom locale collations. The rows are
* ordered by this collation.
* @param aConn
* A connection to either the UTF-8 database or the UTF-16 database.
* @return The resulting strings in an array.
*/
function getResults(aCollation, aConn)
{
let results = [];
let stmt = aConn.createStatement("SELECT t FROM test " +
"ORDER BY t COLLATE " + aCollation + " ASC");
while (stmt.executeStep())
results.push(stmt.row.t);
stmt.finalize();
return results;
}
/**
* Inserts strings into our test table of the given database in the order given.
*
* @param aStrings
* An array of strings.
* @param aConn
* A connection to either the UTF-8 database or the UTF-16 database.
*/
function initTableWithStrings(aStrings, aConn)
{
print("Initializing test table.");
aConn.executeSimpleSQL("DROP TABLE IF EXISTS test");
aConn.createTable("test", "t TEXT");
let stmt = aConn.createStatement("INSERT INTO test (t) VALUES (:t)");
aStrings.forEach(function (str) {
stmt.params.t = str;
stmt.execute();
stmt.reset();
});
stmt.finalize();
}
/**
* Returns a sorting function suitable for passing to Array.prototype.sort().
* The returned function uses the application's locale to compare strings.
*
* @param aCollation
* The name of one of our custom locale collations. The sorting
* strength is computed from this value.
* @return A function to use as a sorting callback.
*/
function localeCompare(aCollation)
{
var strength;
switch (aCollation) {
case "locale":
strength = Ci.nsICollation.kCollationCaseInSensitive;
break;
case "locale_case_sensitive":
strength = Ci.nsICollation.kCollationAccentInsenstive;
break;
case "locale_accent_sensitive":
strength = Ci.nsICollation.kCollationCaseInsensitiveAscii;
break;
case "locale_case_accent_sensitive":
strength = Ci.nsICollation.kCollationCaseSensitive;
break;
default:
do_throw("Error in test: unknown collation '" + aCollation + "'");
break;
}
return function (aStr1, aStr2)
gLocaleCollation.compareString(strength, aStr1, aStr2);
}
/**
* Reads in the test data from the file DATA_BASENAME and returns it as an array
* of strings.
*
* @return The test data as an array of strings.
*/
function readTestData()
{
print("Reading in test data.");
let file = do_get_file(DATA_BASENAME);
let istream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
istream.init(file, -1, -1, 0);
istream.QueryInterface(Components.interfaces.nsILineInputStream);
let line = {};
let lines = [];
while (istream.readLine(line))
lines.push(line.value);
istream.close();
return lines;
}
/**
* Gets the results from the given database using the given collation and
* ensures that they match gStrings sorted by the same collation.
*
* @param aCollation
* The name of one of our custom locale collations. The rows from the
* database and the expected results are ordered by this collation.
* @param aConn
* A connection to either the UTF-8 database or the UTF-16 database.
*/
function runTest(aCollation, aConn)
{
ensureResultsAreCorrect(getResults(aCollation, aConn),
gStrings.slice(0).sort(localeCompare(aCollation)));
}
/**
* Gets the results from the UTF-8 database using the given collation and
* ensures that they match gStrings sorted by the same collation.
*
* @param aCollation
* The name of one of our custom locale collations. The rows from the
* database and the expected results are ordered by this collation.
*/
function runUtf8Test(aCollation)
{
runTest(aCollation, getOpenedDatabase());
}
/**
* Gets the results from the UTF-16 database using the given collation and
* ensures that they match gStrings sorted by the same collation.
*
* @param aCollation
* The name of one of our custom locale collations. The rows from the
* database and the expected results are ordered by this collation.
*/
function runUtf16Test(aCollation)
{
runTest(aCollation, gUtf16Conn);
}
/**
* Sets up the test suite.
*/
function setup()
{
print("-- Setting up the test_locale_collation.js suite.");
gStrings = readTestData();
initTableWithStrings(gStrings, getOpenedDatabase());
gUtf16Conn = createUtf16Database();
initTableWithStrings(gStrings, gUtf16Conn);
let localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
getService(Ci.nsILocaleService);
let collFact = Cc["@mozilla.org/intl/collation-factory;1"].
createInstance(Ci.nsICollationFactory);
gLocaleCollation = collFact.CreateCollation(localeSvc.getApplicationLocale());
}
///////////////////////////////////////////////////////////////////////////////
//// Test Runs
let gTests = [
{
desc: "Case and accent sensitive UTF-8",
run: function () runUtf8Test("locale_case_accent_sensitive")
},
{
desc: "Case sensitive, accent insensitive UTF-8",
run: function () runUtf8Test("locale_case_sensitive")
},
{
desc: "Case insensitive, accent sensitive UTF-8",
run: function () runUtf8Test("locale_accent_sensitive")
},
{
desc: "Case and accent insensitive UTF-8",
run: function () runUtf8Test("locale")
},
{
desc: "Case and accent sensitive UTF-16",
run: function () runUtf16Test("locale_case_accent_sensitive")
},
{
desc: "Case sensitive, accent insensitive UTF-16",
run: function () runUtf16Test("locale_case_sensitive")
},
{
desc: "Case insensitive, accent sensitive UTF-16",
run: function () runUtf16Test("locale_accent_sensitive")
},
{
desc: "Case and accent insensitive UTF-16",
run: function () runUtf16Test("locale")
},
];
function run_test()
{
setup();
gTests.forEach(function (test) {
print("-- Running test: " + test.desc);
test.run();
});
}