Add interface to configure SQLite to use ICU collation functions. (CVS 3936)
FossilOrigin-Name: b29a81b4fbb926fa09186340342848b9fe589033
This commit is contained in:
+106
-4
@@ -21,6 +21,7 @@
|
||||
#include <unicode/utypes.h>
|
||||
#include <unicode/uregex.h>
|
||||
#include <unicode/ustring.h>
|
||||
#include <unicode/ucol.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include "sqlite3.h"
|
||||
@@ -51,6 +52,24 @@ static void xFree(void *p){
|
||||
** http://unicode.org/reports/tr21/tr21-5.html#Caseless_Matching
|
||||
*/
|
||||
|
||||
/*
|
||||
** This function is called when an ICU function called from within
|
||||
** the implementation of an SQL scalar function returns an error.
|
||||
**
|
||||
** The scalar function context passed as the first argument is
|
||||
** loaded with an error message based on the following two args.
|
||||
*/
|
||||
static void icuFunctionError(
|
||||
sqlite3_context *pCtx, /* SQLite scalar function context */
|
||||
const char *zName, /* Name of ICU function that failed */
|
||||
UErrorCode e /* Error code returned by ICU function */
|
||||
){
|
||||
char zBuf[128];
|
||||
sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
||||
zBuf[127] = '\0';
|
||||
sqlite3_result_error(pCtx, zBuf, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Function to delete compiled regexp objects. Registered as
|
||||
** a destructor function with sqlite3_set_auxdata().
|
||||
@@ -104,7 +123,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
||||
}else{
|
||||
assert(!pExpr);
|
||||
sqlite3_result_error(p, "Error compiling regular expression", -1);
|
||||
icuFunctionError(p, "uregex_open", status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -112,14 +131,14 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
/* Configure the text that the regular expression operates on. */
|
||||
uregex_setText(pExpr, zString, -1, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
sqlite3_result_error(p, "Error configuring regular expression", -1);
|
||||
icuFunctionError(p, "uregex_setText", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt the match */
|
||||
res = uregex_matches(pExpr, 0, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
sqlite3_result_error(p, "Error matching regular expression", -1);
|
||||
icuFunctionError(p, "uregex_matches", status);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,13 +209,94 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
}
|
||||
|
||||
if( !U_SUCCESS(status) ){
|
||||
sqlite3_result_error(p, "Error converting case", -1);
|
||||
icuFunctionError(p, "u_strToLower()/u_strToUpper", status);
|
||||
return;
|
||||
}
|
||||
|
||||
sqlite3_result_text16(p, zOutput, -1, xFree);
|
||||
}
|
||||
|
||||
/*
|
||||
** Collation sequence destructor function. The pCtx argument points to
|
||||
** a UCollator structure previously allocated using ucol_open().
|
||||
*/
|
||||
static void icuCollationDel(void *pCtx){
|
||||
UCollator *p = (UCollator *)pCtx;
|
||||
ucol_close(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Collation sequence comparison function. The pCtx argument points to
|
||||
** a UCollator structure previously allocated using ucol_open().
|
||||
*/
|
||||
static int icuCollationColl(
|
||||
void *pCtx,
|
||||
int nLeft,
|
||||
const void *zLeft,
|
||||
int nRight,
|
||||
const void *zRight
|
||||
){
|
||||
UCollationResult res;
|
||||
UCollator *p = (UCollator *)pCtx;
|
||||
res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
|
||||
switch( res ){
|
||||
case UCOL_LESS: return -1;
|
||||
case UCOL_GREATER: return +1;
|
||||
case UCOL_EQUAL: return 0;
|
||||
}
|
||||
assert(!"Bad return value from ucol_strcoll()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the scalar function icu_load_collation().
|
||||
**
|
||||
** This scalar function is used to add ICU collation based collation
|
||||
** types to an SQLite database connection. It is intended to be called
|
||||
** as follows:
|
||||
**
|
||||
** SELECT icu_load_collation(<locale>, <collation-name>);
|
||||
**
|
||||
** Where <locale> is a string containing an ICU locale identifier (i.e.
|
||||
** "en_AU", "tr_TR" etc.) and <collation-name> is the name of the
|
||||
** collation sequence to create.
|
||||
*/
|
||||
static void icuLoadCollation(
|
||||
sqlite3_context *p,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
|
||||
const char *zName; /* SQL Collation sequence name (eg. "japanese") */
|
||||
UCollator *pUCollator; /* ICU library collation object */
|
||||
int rc; /* Return code from sqlite3_create_collation_x() */
|
||||
|
||||
assert(nArg==2);
|
||||
zLocale = (const char *)sqlite3_value_text(apArg[0]);
|
||||
zName = (const char *)sqlite3_value_text(apArg[1]);
|
||||
|
||||
if( !zLocale || !zName ){
|
||||
return;
|
||||
}
|
||||
|
||||
pUCollator = ucol_open(zLocale, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
icuFunctionError(p, "ucol_open", status);
|
||||
return;
|
||||
}
|
||||
assert(p);
|
||||
|
||||
rc = sqlite3_create_collation_x(db, zName, SQLITE_UTF16, (void *)pUCollator,
|
||||
icuCollationColl, icuCollationDel
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
ucol_close(pUCollator);
|
||||
sqlite3_result_error(p, "Error registering collation function", -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the ICU extension functions with database db.
|
||||
*/
|
||||
@@ -219,6 +319,8 @@ int sqlite3IcuInit(sqlite3 *db){
|
||||
{"lower", 2, SQLITE_UTF8, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||||
|
||||
{"icu_load_collation", 2, SQLITE_UTF8, (void*)db, icuLoadCollation},
|
||||
};
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
Reference in New Issue
Block a user