#if !defined(SQLITE_CORE) \
|| defined(SQLITE_ENABLE_ICU) \
|| defined(SQLITE_ENABLE_ICU_COLLATIONS)
#include <unicode/utypes.h>
#include <unicode/uregex.h>
#include <unicode/ustring.h>
#include <unicode/ucol.h>
#include <assert.h>
#ifndef SQLITE_CORE
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#else
#include "sqlite3.h"
#endif
static void icuFunctionError(
sqlite3_context *pCtx,
const char *zName,
UErrorCode e
){
char zBuf[128];
sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
zBuf[127] = '\0';
sqlite3_result_error(pCtx, zBuf, -1);
}
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
#endif
static void xFree(void *p){
sqlite3_free(p);
}
static const unsigned char icuUtf8Trans1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
};
#define SQLITE_ICU_READ_UTF8(zIn, c) \
c = *(zIn++); \
if( c>=0xc0 ){ \
c = icuUtf8Trans1[c-0xc0]; \
while( (*zIn & 0xc0)==0x80 ){ \
c = (c<<6) + (0x3f & *(zIn++)); \
} \
}
#define SQLITE_ICU_SKIP_UTF8(zIn) \
assert( *zIn ); \
if( *(zIn++)>=0xc0 ){ \
while( (*zIn & 0xc0)==0x80 ){zIn++;} \
}
static int icuLikeCompare(
const uint8_t *zPattern,
const uint8_t *zString,
const UChar32 uEsc
){
static const uint32_t MATCH_ONE = (uint32_t)'_';
static const uint32_t MATCH_ALL = (uint32_t)'%';
int prevEscape = 0;
while( 1 ){
uint32_t uPattern;
SQLITE_ICU_READ_UTF8(zPattern, uPattern);
if( uPattern==0 ) break;
if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
uint8_t c;
while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
if( c==MATCH_ONE ){
if( *zString==0 ) return 0;
SQLITE_ICU_SKIP_UTF8(zString);
}
zPattern++;
}
if( *zPattern==0 ) return 1;
while( *zString ){
if( icuLikeCompare(zPattern, zString, uEsc) ){
return 1;
}
SQLITE_ICU_SKIP_UTF8(zString);
}
return 0;
}else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
if( *zString==0 ) return 0;
SQLITE_ICU_SKIP_UTF8(zString);
}else if( uPattern==(uint32_t)uEsc && !prevEscape ){
prevEscape = 1;
}else{
uint32_t uString;
SQLITE_ICU_READ_UTF8(zString, uString);
uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
if( uString!=uPattern ){
return 0;
}
prevEscape = 0;
}
}
return *zString==0;
}
static void icuLikeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *zA = sqlite3_value_text(argv[0]);
const unsigned char *zB = sqlite3_value_text(argv[1]);
UChar32 uEsc = 0;
if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
return;
}
if( argc==3 ){
int nE= sqlite3_value_bytes(argv[2]);
const unsigned char *zE = sqlite3_value_text(argv[2]);
int i = 0;
if( zE==0 ) return;
U8_NEXT(zE, i, nE, uEsc);
if( i!=nE){
sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
return;
}
}
if( zA && zB ){
sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
}
}
static void icuRegexpDelete(void *p){
URegularExpression *pExpr = (URegularExpression *)p;
uregex_close(pExpr);
}
static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
UErrorCode status = U_ZERO_ERROR;
URegularExpression *pExpr;
UBool res;
const UChar *zString = sqlite3_value_text16(apArg[1]);
(void)nArg;
if( !zString ){
return;
}
pExpr = sqlite3_get_auxdata(p, 0);
if( !pExpr ){
const UChar *zPattern = sqlite3_value_text16(apArg[0]);
if( !zPattern ){
return;
}
pExpr = uregex_open(zPattern, -1, 0, 0, &status);
if( U_SUCCESS(status) ){
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
pExpr = sqlite3_get_auxdata(p, 0);
}
if( !pExpr ){
icuFunctionError(p, "uregex_open", status);
return;
}
}
uregex_setText(pExpr, zString, -1, &status);
if( !U_SUCCESS(status) ){
icuFunctionError(p, "uregex_setText", status);
return;
}
res = uregex_matches(pExpr, 0, &status);
if( !U_SUCCESS(status) ){
icuFunctionError(p, "uregex_matches", status);
return;
}
uregex_setText(pExpr, 0, 0, &status);
sqlite3_result_int(p, res ? 1 : 0);
}
static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
const UChar *zInput;
UChar *zOutput = 0;
int nInput;
int nOut;
int cnt;
int bToUpper;
UErrorCode status;
const char *zLocale = 0;
assert(nArg==1 || nArg==2);
bToUpper = (sqlite3_user_data(p)!=0);
if( nArg==2 ){
zLocale = (const char *)sqlite3_value_text(apArg[1]);
}
zInput = sqlite3_value_text16(apArg[0]);
if( !zInput ){
return;
}
nOut = nInput = sqlite3_value_bytes16(apArg[0]);
if( nOut==0 ){
sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
return;
}
for(cnt=0; cnt<2; cnt++){
UChar *zNew = sqlite3_realloc(zOutput, nOut);
if( zNew==0 ){
sqlite3_free(zOutput);
sqlite3_result_error_nomem(p);
return;
}
zOutput = zNew;
status = U_ZERO_ERROR;
if( bToUpper ){
nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
}else{
nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
}
if( U_SUCCESS(status) ){
sqlite3_result_text16(p, zOutput, nOut, xFree);
}else if( status==U_BUFFER_OVERFLOW_ERROR ){
assert( cnt==0 );
continue;
}else{
icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
}
return;
}
assert( 0 );
}
#endif
static void icuCollationDel(void *pCtx){
UCollator *p = (UCollator *)pCtx;
ucol_close(p);
}
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(!"Unexpected return value from ucol_strcoll()");
return 0;
}
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;
const char *zName;
UCollator *pUCollator;
int rc;
assert(nArg==2);
(void)nArg;
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_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
icuCollationColl, icuCollationDel
);
if( rc!=SQLITE_OK ){
ucol_close(pUCollator);
sqlite3_result_error(p, "Error registering collation function", -1);
}
}
int sqlite3IcuInit(sqlite3 *db){
# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
static const struct IcuScalar {
const char *zName;
unsigned char nArg;
unsigned int enc;
unsigned char iContext;
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} scalars[] = {
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
{"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
{"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
{"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
#endif
};
int rc = SQLITE_OK;
int i;
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
const struct IcuScalar *p = &scalars[i];
rc = sqlite3_create_function(
db, p->zName, p->nArg, p->enc,
p->iContext ? (void*)db : (void*)0,
p->xFunc, 0, 0
);
}
return rc;
}
#if !SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi)
return sqlite3IcuInit(db);
}
#endif
#endif