diff options
author | danielk1977 <danielk1977@noemail.net> | 2007-05-07 09:32:45 +0000 |
---|---|---|
committer | danielk1977 <danielk1977@noemail.net> | 2007-05-07 09:32:45 +0000 |
commit | a9808b31a8c3ebd8fecc7c63afdec7519ccdafd1 (patch) | |
tree | bc3a302d4075e3b9925f115ddfc4b0acf5552a64 /src | |
parent | a58906a8dd74d751c4c9609f828f9043c1a983b1 (diff) | |
download | sqlite-a9808b31a8c3ebd8fecc7c63afdec7519ccdafd1.tar.gz sqlite-a9808b31a8c3ebd8fecc7c63afdec7519ccdafd1.zip |
Add the experimental create_collation_x() api. (CVS 3934)
FossilOrigin-Name: ff49d48f2f025898a0f4ace1fc227e1d367ea89f
Diffstat (limited to 'src')
-rw-r--r-- | src/callback.c | 3 | ||||
-rw-r--r-- | src/main.c | 63 | ||||
-rw-r--r-- | src/sqlite.h.in | 14 | ||||
-rw-r--r-- | src/sqliteInt.h | 11 | ||||
-rw-r--r-- | src/test1.c | 106 |
5 files changed, 178 insertions, 19 deletions
diff --git a/src/callback.c b/src/callback.c index 87f5b2472..f3d418808 100644 --- a/src/callback.c +++ b/src/callback.c @@ -13,7 +13,7 @@ ** This file contains functions used to access the internal hash tables ** of user defined functions and collation sequences. ** -** $Id: callback.c,v 1.17 2007/04/16 15:06:25 danielk1977 Exp $ +** $Id: callback.c,v 1.18 2007/05/07 09:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -63,6 +63,7 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){ pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, n, 0); if( pColl2->xCmp!=0 ){ memcpy(pColl, pColl2, sizeof(CollSeq)); + pColl->xDel = 0; /* Do not copy the destructor */ return SQLITE_OK; } } diff --git a/src/main.c b/src/main.c index 38aa70bb1..bd72bdd97 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.371 2007/05/06 16:04:12 danielk1977 Exp $ +** $Id: main.c,v 1.372 2007/05/07 09:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -182,6 +182,12 @@ int sqlite3_close(sqlite3 *db){ for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ CollSeq *pColl = (CollSeq *)sqliteHashData(i); + /* Invoke any destructors registered for collation sequence user data. */ + for(j=0; j<3; j++){ + if( pColl[j].xDel ){ + pColl[j].xDel(pColl[j].pUser); + } + } sqliteFree(pColl); } sqlite3HashClear(&db->aCollSeq); @@ -825,7 +831,8 @@ static int createCollation( const char *zName, int enc, void* pCtx, - int(*xCompare)(void*,int,const void*,int,const void*) + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDel)(void*) ){ CollSeq *pColl; int enc2; @@ -860,12 +867,33 @@ static int createCollation( return SQLITE_BUSY; } sqlite3ExpirePreparedStatements(db); + + /* If collation sequence pColl was created directly by a call to + ** sqlite3_create_collation, and not generated by synthCollSeq(), + ** then any copies made by synthCollSeq() need to be invalidated. + ** Also, collation destructor - CollSeq.xDel() - function may need + ** to be called. + */ + if( (pColl->enc & ~SQLITE_UTF16_ALIGNED)==enc2 ){ + CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName, strlen(zName)); + int j; + for(j=0; j<3; j++){ + CollSeq *p = &aColl[j]; + if( p->enc==pColl->enc ){ + if( p->xDel ){ + p->xDel(p->pUser); + } + p->xCmp = 0; + } + } + } } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, strlen(zName), 1); if( pColl ){ pColl->xCmp = xCompare; pColl->pUser = pCtx; + pColl->xDel = xDel; pColl->enc = enc2 | (enc & SQLITE_UTF16_ALIGNED); } sqlite3Error(db, SQLITE_OK, 0); @@ -915,9 +943,9 @@ static int openDatabase( ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. */ - if( createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc) || - createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc) || - createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc) || + if( createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0) || + createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0) || + createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0) || (db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 6, 0))==0 ){ assert( sqlite3MallocFailed() ); @@ -926,7 +954,7 @@ static int openDatabase( } /* Also add a UTF-8 case-insensitive collation sequence. */ - createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc); + createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); /* Set flags on the built-in collating sequences */ db->pDfltColl->type = SQLITE_COLL_BINARY; @@ -986,7 +1014,7 @@ static int openDatabase( #endif #ifdef SQLITE_ENABLE_ICU - { + if( !sqlite3MallocFailed() ){ extern int sqlite3IcuInit(sqlite3*); sqlite3IcuInit(db); } @@ -1106,7 +1134,24 @@ int sqlite3_create_collation( ){ int rc; assert( !sqlite3MallocFailed() ); - rc = createCollation(db, zName, enc, pCtx, xCompare); + rc = createCollation(db, zName, enc, pCtx, xCompare, 0); + return sqlite3ApiExit(db, rc); +} + +/* +** Register a new collation sequence with the database handle db. +*/ +int sqlite3_create_collation_x( + sqlite3* db, + const char *zName, + int enc, + void* pCtx, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDel)(void*) +){ + int rc; + assert( !sqlite3MallocFailed() ); + rc = createCollation(db, zName, enc, pCtx, xCompare, xDel); return sqlite3ApiExit(db, rc); } @@ -1126,7 +1171,7 @@ int sqlite3_create_collation16( assert( !sqlite3MallocFailed() ); zName8 = sqlite3utf16to8(zName, -1); if( zName8 ){ - rc = createCollation(db, zName8, enc, pCtx, xCompare); + rc = createCollation(db, zName8, enc, pCtx, xCompare, 0); sqliteFree(zName8); } return sqlite3ApiExit(db, rc); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 4af11e26c..f84128c55 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.204 2007/05/03 16:31:26 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.205 2007/05/07 09:32:45 danielk1977 Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1280,6 +1280,18 @@ int sqlite3_create_collation16( ); /* +****** EXPERIMENTAL - subject to change without notice ************** +*/ +int sqlite3_create_collation_x( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDel)(void*) +); + +/* ** To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the ** database handle to be called whenever an undefined collation sequence is diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c5917c643..374f1ebe1 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.557 2007/05/05 11:48:54 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.558 2007/05/07 09:32:45 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -646,11 +646,12 @@ struct Column { ** collating sequence may not be read or written. */ struct CollSeq { - char *zName; /* Name of the collating sequence, UTF-8 encoded */ - u8 enc; /* Text encoding handled by xCmp() */ - u8 type; /* One of the SQLITE_COLL_... values below */ - void *pUser; /* First argument to xCmp() */ + char *zName; /* Name of the collating sequence, UTF-8 encoded */ + u8 enc; /* Text encoding handled by xCmp() */ + u8 type; /* One of the SQLITE_COLL_... values below */ + void *pUser; /* First argument to xCmp() */ int (*xCmp)(void*,int, const void*, int, const void*); + void (*xDel)(void*); /* Destructor for pUser */ }; /* diff --git a/src/test1.c b/src/test1.c index e4221b110..479390dea 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.247 2007/05/06 16:04:12 danielk1977 Exp $ +** $Id: test1.c,v 1.248 2007/05/07 09:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -1508,6 +1508,18 @@ static int test_table_column_metadata( /* ** sqlite3_blob_read CHANNEL OFFSET N +** +** This command is used to test the sqlite3_blob_read() in ways that +** the Tcl channel interface does not. The first argument should +** be the name of a valid channel created by the [incrblob] method +** of a database handle. This function calls sqlite3_blob_read() +** to read N bytes from offset OFFSET from the underlying SQLite +** blob handle. +** +** On success, a byte-array object containing the read data is +** returned. On failure, the interpreter result is set to the +** text representation of the returned error code (i.e. "SQLITE_NOMEM") +** and a Tcl exception is thrown. */ static int test_blob_read( ClientData clientData, /* Not used */ @@ -1555,6 +1567,17 @@ static int test_blob_read( /* ** sqlite3_blob_write CHANNEL OFFSET DATA +** +** This command is used to test the sqlite3_blob_write() in ways that +** the Tcl channel interface does not. The first argument should +** be the name of a valid channel created by the [incrblob] method +** of a database handle. This function calls sqlite3_blob_write() +** to write the DATA byte-array to the underlying SQLite blob handle. +** at offset OFFSET. +** +** On success, an empty string is returned. On failure, the interpreter +** result is set to the text representation of the returned error code +** (i.e. "SQLITE_NOMEM") and a Tcl exception is thrown. */ static int test_blob_write( ClientData clientData, /* Not used */ @@ -1599,6 +1622,82 @@ static int test_blob_write( #endif /* +** Usage: sqlite3_create_collation_x DB-HANDLE NAME CMP-PROC DEL-PROC +** +** This Tcl proc is used for testing the experimental +** sqlite3_create_collation_x() interface. +*/ +struct TestCollationX { + Tcl_Interp *interp; + Tcl_Obj *pCmp; + Tcl_Obj *pDel; +}; +typedef struct TestCollationX TestCollationX; +static void testCreateCollationDel(void *pCtx){ + TestCollationX *p = (TestCollationX *)pCtx; + + int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); + if( rc!=TCL_OK ){ + Tcl_BackgroundError(p->interp); + } + + Tcl_DecrRefCount(p->pCmp); + Tcl_DecrRefCount(p->pDel); + sqlite3_free((void *)p); +} +static int testCreateCollationCmp( + void *pCtx, + int nLeft, + const void *zLeft, + int nRight, + const void *zRight +){ + TestCollationX *p = (TestCollationX *)pCtx; + Tcl_Obj *pScript = Tcl_DuplicateObj(p->pCmp); + int iRes = 0; + + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zLeft, nLeft)); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zRight,nRight)); + + if( TCL_OK!=Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL) + || TCL_OK!=Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iRes) + ){ + Tcl_BackgroundError(p->interp); + } + Tcl_DecrRefCount(pScript); + + return iRes; +} +static int test_create_collation_x( + ClientData clientData, /* Not used */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + TestCollationX *p; + sqlite3 *db; + + if( objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB-HANDLE NAME CMP-PROC DEL-PROC"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + + p = (TestCollationX *)sqlite3_malloc(sizeof(TestCollationX)); + p->pCmp = objv[3]; + p->pDel = objv[4]; + p->interp = interp; + Tcl_IncrRefCount(p->pCmp); + Tcl_IncrRefCount(p->pDel); + + sqlite3_create_collation_x(db, Tcl_GetString(objv[2]), SQLITE_UTF8, + (void *)p, testCreateCollationCmp, testCreateCollationDel + ); + return TCL_OK; +} + +/* ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC? */ static int test_load_extension( @@ -4617,8 +4716,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ {"sqlite3_column_origin_name16", test_stmt_utf16, sqlite3_column_origin_name16}, #endif #endif - { "sqlite3_global_recover", test_global_recover, 0 }, - { "working_64bit_int", working_64bit_int, 0 }, + { "sqlite3_create_collation_x", test_create_collation_x, 0 }, + { "sqlite3_global_recover", test_global_recover, 0 }, + { "working_64bit_int", working_64bit_int, 0 }, /* Functions from os.h */ #ifndef SQLITE_OMIT_DISKIO |