1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Some progress on user-defined collation sequences. (CVS 1544)

FossilOrigin-Name: c634e71f1909819fb55c728bc410e5cc390428e3
This commit is contained in:
danielk1977
2004-06-09 09:55:16 +00:00
parent 80242055e5
commit 0202b29ef7
24 changed files with 2456 additions and 293 deletions

View File

@ -1,5 +1,5 @@
C Start\sall\stransactions\sand\sverify\sall\sschema\scookies\snear\sthe\sbeginning\sof\nof\seach\svdbe\sprogram.\s(CVS\s1543)
D 2004-06-09T00:48:12
C Some\sprogress\son\suser-defined\scollation\ssequences.\s(CVS\s1544)
D 2004-06-09T09:55:17
F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@ -27,17 +27,17 @@ F src/attach.c e76e4590ec5dd389e5646b171881b5243a6ef391
F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79
F src/btree.c edb38affc2e83f4299e49104cfe14e6570d8bd32
F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa
F src/build.c f720a2538af6b39a0edc9f62fad3a8c809d455b5
F src/build.c 763cb50521ee88873522ae2e836b121f312d2bb2
F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2
F src/delete.c b30f08250c9ed53a25a13c7c04599c1e8753992d
F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37
F src/expr.c 2b18dd4ed178e39989f22d75bf0e68ba6ed3923c
F src/expr.c 3aea8faac17debea4f5c2659351c27d5660453a0
F src/func.c ffbdfa4cad2a16a41390c2ce923ef8b0f173d777
F src/hash.c 440c2f8cb373ee1b4e13a0988489c7cd95d55b6f
F src/hash.h 762d95f1e567664d1eafc1687de755626be962fb
F src/insert.c 4268d9e3959cc845ea243fb4ec7507269404dad9
F src/legacy.c ad23746f15f67e34577621b1875f639c94839e1f
F src/main.c f94886765e71082d020c9d7b4d4f8645029ee7f6
F src/main.c ac8a6e871be5917775d2728b9abbb1659d59f8fd
F src/md5.c 4302e84ae516c616bb079c4e6d038c0addb33481
F src/os.h a3a9c2df29acbff54aef742e85c302d23634019f
F src/os_common.h 12074232439f904b3805beeff1245bd1b5532994
@ -49,34 +49,34 @@ F src/os_win.c a13b85a0d4889e3d0b254ed2a61354acddc59fc4
F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c
F src/pager.c 3fddd1e5b3e449b19e4f762ab1f1d10786d56d28
F src/pager.h 0c7b5ac45c69e690c45d160d03bdc8fbd2d4657b
F src/parse.y 19972fbaa3440f511da36eae1d1fe536fe2c7805
F src/parse.y 097438674976355a10cf177bd97326c548820b86
F src/pragma.c 9328a31c22615758077e8ded1126affe8f5e7fbe
F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
F src/select.c 0ac0adeb2ae15255ac4399d9ee1b0d25a266a676
F src/select.c 1f8355e702f109f6771f82a9bfe7aac4c82cbaf2
F src/shell.c ca519519dcbbc582f6d88f7d0e7583b857fd3469
F src/sqlite.h.in 577974e9a1b85815ccddfb5b858695b62000f595
F src/sqliteInt.h 472033b41fe76cf0a9c39dda410cedbadeb61b03
F src/sqlite.h.in 6ad05abc7fd72da74691e1eb45f0eff4117eba4e
F src/sqliteInt.h 55525f9a2d7b238ea4a89c04ae86fe9339452d6d
F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2
F src/tclsqlite.c c4d549ad9f5941288247759ead89d9632254fdc3
F src/test1.c 416a37411f2ca7947e6509c5b6ddc6dd86a1204e
F src/tclsqlite.c f5c5116720baefb7de5d6acf18baedf1e42756cc
F src/test1.c 73770f2cd2bf97027e2edf62ee345e90c78dfcef
F src/test2.c ae18537d8a85e5028c955837797f9da461b908b8
F src/test3.c beafd0ccf7b9ae784744be1b1e66ffe8f64c25da
F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2
F src/test5.c 44178ce85c3afd2004ab4eeb5cfd7487116ce366
F src/test5.c 862784cd7a68e7d36f00287aac6e413ca996eaf8
F src/tokenize.c 183c5d7da11affab5d70d903d33409c8c0ce6c5b
F src/trigger.c 2c28bf37f21e1ca2fc39cd88db8dbe3ad6ac5419
F src/update.c 259f06e7b22c684b2d3dda54a18185892d6e9573
F src/utf.c c2c8e445bfea724f3502609d6389fe66651f02ab
F src/util.c 8b3680271111bcdf5b395916b08b9a6684e0e73d
F src/util.c ad379519d8267ea1f626874ad4e629e982e8ca58
F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f
F src/vdbe.c 3ffa1effb57f861131f1abc6d4f14db81fad2ade
F src/vdbe.c fec13be8b6f03158bfb3069c7bd6182eb3ef4fca
F src/vdbe.h 46f74444a213129bc4b5ce40124dd8ed613b0cde
F src/vdbeInt.h ab592f23ed5a1913f9a506bd7b76c5e39377942a
F src/vdbeapi.c 4ac95766b0515538037a7aec172ed26142f97cf9
F src/vdbeaux.c ab8c99fd5c94ff366f0db3801eb7a1d3f4026e23
F src/vdbemem.c 5d029d83bc60eaf9c45837fcbc0b03348ec95d7a
F src/where.c ded00b92dcee77ebe358ff48f5ef05ee8e8ff163
F src/vdbeaux.c 8547c18b160f3e02fb14e90f0162c195af702c00
F src/vdbemem.c 04502b81039f9a2b1c9a096e894eecf6d4877508
F src/where.c 32578882a245f8ac3303c5cea4664cd51fc73891
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
F test/attach.test aed659e52635662bcd5069599aaca823533edf5a
F test/attach2.test 2185dce04ef9ceb7b2d3df7d17fb2c3817028dea
@ -94,6 +94,9 @@ F test/btree6.test a5ede6bfbbb2ec8b27e62813612c0f28e8f3e027
F test/btree7.test 429b96cfef5b51a7d512cfb4b5b3e453384af293
F test/capi2.test 8fb64e8ab7f78b8254cd4d04bb96822167f731b2
F test/capi3.test b6fe8a66d2ffe28d4faaaec154a143131e8ff631
F test/collate1.test 7f1ad4c24ea949b7a7aee387df7839389990a998
F test/collate2.test 5b92d795048794266ac27f242a411da8ffeaae25
F test/collate4.test a8f2d58bd6943ed1746639c11b12896ccfe8f646
F test/conflict.test 45ce1e44ea748944aed233df8c278a9e1c4c87cc
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/date.test aed5030482ebc02bd8d386c6c86a29f694ab068d
@ -122,7 +125,7 @@ F test/malloc.test 4e19b96d9cd7d61f4826e6fce03849ad5ad526dd
F test/memdb.test 6ece25c7c0e6500199d3662607a3edca081abb2a
F test/memleak.test 4d5d374c8ea1fc5ac634aed58cac1047848ce65e
F test/minmax.test 9dcf52f713b1b9e61d0a88a51eb8bb2e3c52d0ab
F test/misc1.test 508ef7287490900570054dfd853641ec4ef66cd6
F test/misc1.test 06e92b613615d38919bf6fea85536bc685a3da4b
F test/misc2.test 10c2ce26407d37411b96273e552d5095393732be
F test/misc3.test eb488314990bfc0959221a1acc465013238bf168
F test/misuse.test 1095f26d1aed406c65e1d2eba651c4bb7c38cbff
@ -142,11 +145,11 @@ F test/select3.test f8ff60d98c7b4898f5e7326f0c5929ba56f5d047
F test/select4.test 86e72fc3b07de4fe11439aa419e37db3c49467e2
F test/select5.test 3f3f0f31e674fa61f8a3bdb6af1517dfae674081
F test/select6.test a9e31906e700e7c7592c4d0acfc022808f718baf
F test/sort.test 863f07884fb68a70b738998e9a548132a0d0b7d9
F test/sort.test dbd94673b05054e44ca3f08a80faa1e890ef06d8
F test/subselect.test f0fea8cf9f386d416d64d152e3c65f9116d0f50f
F test/table.test 1defd90e8200661a822618b18748b9339a7fef2f
F test/tableapi.test e0c4cce61e58343caa84dab33fa6823cb35fe1e1
F test/tclsqlite.test 853e4372004d209e2ff0d4b0b35c91d4f64cd7d8
F test/tclsqlite.test ab5e5c5a00b592c48c38fec8095cde74186c3fc2
F test/temptable.test a770ba6308d7f7332fce985086b8e06bed6430c2
F test/tester.tcl fc10520db0d3ce4ef6a8b5ab91bd102fc3f4280a
F test/thread1.test 2fd5c10e82434f6b03be77b61fde5a050668abf2
@ -194,7 +197,7 @@ F www/conflict.tcl fb8a2ba83746c7fdfd9e52fa7f6aaf5c422b8246
F www/copyright-release.html 294e011760c439c44951a6bfecd4c81a1ae359e8
F www/copyright-release.pdf cfca3558fc97095e57c6117d08f1f5b80d95125a
F www/copyright.tcl 82c9670c7ddb0311912ab7fe24703f33c531066c
F www/datatype3.tcl 22f59662aa4b7a246ef56ee1bf40c514fc06e21c
F www/datatype3.tcl 547a852a4c5970e431e7e3c0ae4b1b3ce82c2063
F www/datatypes.tcl 566004b81c36877397ddbe6e1907aae6065f6b41
F www/docs.tcl 0dcbf954907bd5dbfb7f1e0220f4e50516e07cd3
F www/download.tcl 8c84f15695c92cb01486930055fdf5192995f474
@ -215,7 +218,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248
F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
P 4dfdea7373f3471d17498da3d6c3aaf926a72d4b
R 6bf109928e5c77863915e3d9ce0daa3e
U drh
Z 588abfea4494baa41bec5420972c8c24
P 1086196460e261718e78512d77e25dde021a117d
R 20d537aca227a4420895d927bae8916e
U danielk1977
Z 480a8bf4fe8242d162be76603ac93c57

View File

@ -1 +1 @@
1086196460e261718e78512d77e25dde021a117d
c634e71f1909819fb55c728bc410e5cc390428e3

View File

@ -23,7 +23,7 @@
** ROLLBACK
** PRAGMA
**
** $Id: build.c,v 1.210 2004/06/09 00:48:12 drh Exp $
** $Id: build.c,v 1.211 2004/06/09 09:55:17 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -784,7 +784,7 @@ void sqlite3AddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
** If the key is not an INTEGER PRIMARY KEY, then create a unique
** index for the key. No index is created for INTEGER PRIMARY KEYs.
*/
void sqlite3AddPrimaryKey(Parse *pParse, IdList *pList, int onError){
void sqlite3AddPrimaryKey(Parse *pParse, ExprList *pList, int onError){
Table *pTab = pParse->pNewTable;
char *zType = 0;
int iCol = -1, i;
@ -799,7 +799,7 @@ void sqlite3AddPrimaryKey(Parse *pParse, IdList *pList, int onError){
iCol = pTab->nCol - 1;
pTab->aCol[iCol].isPrimKey = 1;
}else{
for(i=0; i<pList->nId; i++){
for(i=0; i<pList->nExpr; i++){
for(iCol=0; iCol<pTab->nCol; iCol++){
if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
break;
@ -807,7 +807,7 @@ void sqlite3AddPrimaryKey(Parse *pParse, IdList *pList, int onError){
}
if( iCol<pTab->nCol ) pTab->aCol[iCol].isPrimKey = 1;
}
if( pList->nId>1 ) iCol = -1;
if( pList->nExpr>1 ) iCol = -1;
}
if( iCol>=0 && iCol<pTab->nCol ){
zType = pTab->aCol[iCol].zType;
@ -821,79 +821,84 @@ void sqlite3AddPrimaryKey(Parse *pParse, IdList *pList, int onError){
}
primary_key_exit:
sqlite3IdListDelete(pList);
sqlite3ExprListDelete(pList);
return;
}
/*
** Return a pointer to CollSeq given the name of a collating sequence.
** If the collating sequence did not previously exist, create it but
** assign it an NULL comparison function.
*/
CollSeq *sqlite3CollateType(Parse *pParse, const char *zType, int nType){
CollSeq *pColl;
sqlite *db = pParse->db;
pColl = sqlite3HashFind(&db->aCollSeq, zType, nType);
if( pColl==0 ){
sqlite3ChangeCollatingFunction(db, zType, nType, 0, 0);
pColl = sqlite3HashFind(&db->aCollSeq, zType, nType);
}
return pColl;
}
/*
** Set the collation function of the most recently parsed table column
** to the CollSeq given.
*/
void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){
Table *p;
Index *pIdx;
CollSeq *pColl;
sqlite *db = pParse->db;
int i;
if( (p = pParse->pNewTable)==0 ) return;
pColl = sqlite3HashFind(&db->aCollSeq, zType, nType);
if( pColl==0 ){
pColl = sqlite3ChangeCollatingFunction(db, zType, nType, 0, 0);
}
if( pColl ){
p->aCol[p->nCol-1].pColl = pColl;
i = p->nCol-1;
pColl = sqlite3LocateCollSeq(pParse, zType, nType);
p->aCol[i].pColl = pColl;
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
** then an index may have been created on this column before the
** collation type was added. Correct this if it is the case.
*/
for(pIdx = p->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->nColumn==1 );
if( pIdx->aiColumn[0]==i ) pIdx->keyInfo.aColl[0] = pColl;
}
}
/*
** Create or modify a collating sequence entry in the sqlite.aCollSeq
** table.
** Locate and return an entry from the db.aCollSeq hash table. If the entry
** specified by zName and nName is not found and parameter 'create' is
** true, then create a new entry.
**
** Once an entry is added to the sqlite.aCollSeq table, it can never
** be removed, though is comparison function or user data can be changed.
** FIX ME: For now, return NULL if create is not true and the entry is not
** found. But this needs to change to call the collation factory.
**
** Return a pointer to the collating function that was created or modified.
** FIX ME: If we have a UTF-8 version of the collation function, and a
** UTF-16 version would be better, should the collation factory be called?
** If so should a flag be set to say that we already requested such a
** function and couldn't get one?
*/
CollSeq *sqlite3ChangeCollatingFunction(
sqlite *db, /* Database into which to insert the collation */
const char *zName, /* Name of the collation */
int nName, /* Number of characters in zName */
void *pUser, /* First argument to xCmp */
int (*xCmp)(void*,int,const void*,int,const void*) /* Comparison function */
CollSeq *sqlite3FindCollSeq(
sqlite *db,
const char *zName,
int nName,
int create
){
CollSeq *pColl;
if( nName<0 ) nName = strlen(zName);
pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
if( pColl==0 ){
pColl = sqliteMallocRaw( sizeof(*pColl) + nName + 1 );
if( pColl==0 ){
return 0;
}
if( 0==pColl && create ){
pColl = sqliteMalloc( sizeof(*pColl) + nName + 1 );
if( pColl ){
pColl->zName = (char*)&pColl[1];
memcpy(pColl->zName, zName, nName+1);
sqlite3HashInsert(&db->aCollSeq, pColl->zName, nName, pColl);
}
pColl->pUser = pUser;
pColl->xCmp = xCmp;
}
return pColl;
}
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
CollSeq *pColl = sqlite3FindCollSeq(pParse->db, zName, nName, 0);
if( !pColl ){
if( pParse->nErr==0 ){
sqlite3SetNString(&pParse->zErrMsg,
"no such collation sequence: ", -1,
zName, nName, 0);
}
pParse->nErr++;
}
return pColl;
}
/*
** Scan the column type name zType (length nType) and return the
** associated affinity type.
@ -1540,9 +1545,9 @@ exit_drop_table:
*/
void sqlite3CreateForeignKey(
Parse *pParse, /* Parsing context */
IdList *pFromCol, /* Columns in this table that point to other table */
ExprList *pFromCol, /* Columns in this table that point to other table */
Token *pTo, /* Name of the other table */
IdList *pToCol, /* Columns in the other table */
ExprList *pToCol, /* Columns in the other table */
int flags /* Conflict resolution algorithms. */
){
Table *p = pParse->pNewTable;
@ -1557,24 +1562,24 @@ void sqlite3CreateForeignKey(
if( pFromCol==0 ){
int iCol = p->nCol-1;
if( iCol<0 ) goto fk_end;
if( pToCol && pToCol->nId!=1 ){
if( pToCol && pToCol->nExpr!=1 ){
sqlite3ErrorMsg(pParse, "foreign key on %s"
" should reference only one column of table %T",
p->aCol[iCol].zName, pTo);
goto fk_end;
}
nCol = 1;
}else if( pToCol && pToCol->nId!=pFromCol->nId ){
}else if( pToCol && pToCol->nExpr!=pFromCol->nExpr ){
sqlite3ErrorMsg(pParse,
"number of columns in foreign key does not match the number of "
"columns in the referenced table");
goto fk_end;
}else{
nCol = pFromCol->nId;
nCol = pFromCol->nExpr;
}
nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1;
if( pToCol ){
for(i=0; i<pToCol->nId; i++){
for(i=0; i<pToCol->nExpr; i++){
nByte += strlen(pToCol->a[i].zName) + 1;
}
}
@ -1631,8 +1636,8 @@ void sqlite3CreateForeignKey(
fk_end:
sqliteFree(pFKey);
sqlite3IdListDelete(pFromCol);
sqlite3IdListDelete(pToCol);
sqlite3ExprListDelete(pFromCol);
sqlite3ExprListDelete(pToCol);
}
/*
@ -1665,8 +1670,8 @@ void sqlite3CreateIndex(
Parse *pParse, /* All information about this parse */
Token *pName1, /* First part of index name. May be NULL */
Token *pName2, /* Second part of index name. May be NULL */
SrcList *pTblName, /* Name of the table to index. Use pParse->pNewTable if 0 */
IdList *pList, /* A list of columns to be indexed */
SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */
ExprList *pList, /* A list of columns to be indexed */
int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */
Token *pEnd /* The ")" that closes the CREATE INDEX statement */
@ -1794,7 +1799,7 @@ void sqlite3CreateIndex(
if( pList==0 ){
nullId.z = pTab->aCol[pTab->nCol-1].zName;
nullId.n = strlen(nullId.z);
pList = sqlite3IdListAppend(0, &nullId);
pList = sqlite3ExprListAppend(0, 0, &nullId);
if( pList==0 ) goto exit_create_index;
}
@ -1802,13 +1807,13 @@ void sqlite3CreateIndex(
** Allocate the index structure.
*/
pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + 1 +
(sizeof(int) + sizeof(CollSeq*))*pList->nId );
(sizeof(int) + sizeof(CollSeq*))*pList->nExpr );
if( pIndex==0 ) goto exit_create_index;
pIndex->aiColumn = (int*)&pIndex->keyInfo.aColl[pList->nId];
pIndex->zName = (char*)&pIndex->aiColumn[pList->nId];
pIndex->aiColumn = (int*)&pIndex->keyInfo.aColl[pList->nExpr];
pIndex->zName = (char*)&pIndex->aiColumn[pList->nExpr];
strcpy(pIndex->zName, zName);
pIndex->pTable = pTab;
pIndex->nColumn = pList->nId;
pIndex->nColumn = pList->nExpr;
pIndex->onError = onError;
pIndex->autoIndex = pName==0;
pIndex->iDb = iDb;
@ -1817,7 +1822,7 @@ void sqlite3CreateIndex(
** load the column indices into the Index structure. Report an error
** if any column is not found.
*/
for(i=0; i<pList->nId; i++){
for(i=0; i<pList->nExpr; i++){
for(j=0; j<pTab->nCol; j++){
if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[j].zName)==0 ) break;
}
@ -1828,9 +1833,15 @@ void sqlite3CreateIndex(
goto exit_create_index;
}
pIndex->aiColumn[i] = j;
if( pList->a[i].pExpr ){
assert( pList->a[i].pExpr->pColl );
pIndex->keyInfo.aColl[i] = pList->a[i].pExpr->pColl;
}else{
pIndex->keyInfo.aColl[i] = pTab->aCol[j].pColl;
}
pIndex->keyInfo.nField = pList->nId;
assert( pIndex->keyInfo.aColl[i] );
}
pIndex->keyInfo.nField = pList->nExpr;
/* Link the new Index structure to its table and to the other
** in-memory database structures.
@ -1915,7 +1926,11 @@ void sqlite3CreateIndex(
}
sqlite3VdbeAddOp(v, OP_String8, 0, 0);
if( pStart && pEnd ){
if( onError==OE_None ){
sqlite3VdbeChangeP3(v, -1, "CREATE INDEX ", P3_STATIC);
}else{
sqlite3VdbeChangeP3(v, -1, "CREATE UNIQUE INDEX ", P3_STATIC);
}
sqlite3VdbeAddOp(v, OP_String8, 0, 0);
n = Addr(pEnd->z) - Addr(pName->z) + 1;
sqlite3VdbeChangeP3(v, -1, pName->z, n);
@ -1950,7 +1965,7 @@ void sqlite3CreateIndex(
/* Clean up before exiting */
exit_create_index:
sqlite3IdListDelete(pList);
sqlite3ExprListDelete(pList);
/* sqlite3SrcListDelete(pTable); */
sqliteFree(zName);
return;

View File

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.136 2004/06/06 12:41:50 danielk1977 Exp $
** $Id: expr.c,v 1.137 2004/06/09 09:55:18 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -55,6 +55,20 @@ char sqlite3ExprAffinity(Expr *pExpr){
return pExpr->affinity;
}
/*
** Return the default collation sequence for the expression pExpr. If
** there is no default collation type, return 0.
*/
CollSeq *sqlite3ExprCollSeq(Expr *pExpr){
if( pExpr ){
if( pExpr->pColl ) return pExpr->pColl;
if( pExpr->op==TK_AS ){
return sqlite3ExprCollSeq(pExpr->pLeft);
}
}
return 0;
}
/*
** pExpr is the left operand of a comparison operator. aff2 is the
** type affinity of the right operand. This routine returns the
@ -134,6 +148,23 @@ static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
return (((int)sqlite3CompareAffinity(pExpr1, aff))<<8)+(jumpIfNull?1:0);
}
/*
** Return a pointer to the collation sequence that should be used by
** a binary comparison operator comparing pLeft and pRight.
**
** If the left hand expression has a collating sequence type, then it is
** used. Otherwise the collation sequence for the right hand expression
** is used, or the default (BINARY) if neither expression has a collating
** type.
*/
static CollSeq* binaryCompareCollSeq(Expr *pLeft, Expr *pRight){
CollSeq *pColl = sqlite3ExprCollSeq(pLeft);
if( !pColl ){
pColl = sqlite3ExprCollSeq(pRight);
}
return pColl;
}
/*
** Construct a new expression node and return a pointer to it. Memory
** for this node is obtained from sqliteMalloc(). The calling function
@ -588,6 +619,7 @@ static int lookupName(
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
pExpr->affinity = pTab->aCol[j].affinity;
pExpr->pColl = pTab->aCol[j].pColl;
break;
}
}
@ -620,6 +652,7 @@ static int lookupName(
cnt++;
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
pExpr->affinity = pTab->aCol[j].affinity;
pExpr->pColl = pTab->aCol[j].pColl;
break;
}
}
@ -798,6 +831,7 @@ int sqlite3ExprResolveIds(
char affinity;
Vdbe *v = sqlite3GetVdbe(pParse);
KeyInfo keyInfo;
int addr; /* Address of OP_OpenTemp instruction */
if( v==0 ) return 1;
if( sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){
@ -819,11 +853,9 @@ int sqlite3ExprResolveIds(
** is used.
*/
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 0);
memset(&keyInfo, 0, sizeof(keyInfo));
keyInfo.nField = 1;
keyInfo.aColl[0] = pParse->db->pDfltColl;
sqlite3VdbeOp3(v, OP_OpenTemp, pExpr->iTable, 0, \
(char*)&keyInfo, P3_KEYINFO);
sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1);
if( pExpr->pSelect ){
@ -835,6 +867,10 @@ int sqlite3ExprResolveIds(
int iParm = pExpr->iTable + (((int)affinity)<<16);
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0);
if( pExpr->pSelect->pEList && pExpr->pSelect->pEList->nExpr>0 ){
keyInfo.aColl[0] = binaryCompareCollSeq(pExpr->pLeft,
pExpr->pSelect->pEList->a[0].pExpr);
}
}else if( pExpr->pList ){
/* Case 2: expr IN (exprlist)
**
@ -849,6 +885,7 @@ int sqlite3ExprResolveIds(
affinity = SQLITE_AFF_NUMERIC;
}
affStr = sqlite3AffinityString(affinity);
keyInfo.aColl[0] = pExpr->pLeft->pColl;
/* Loop through each expression in <exprlist>. */
for(i=0; i<pExpr->pList->nExpr; i++){
@ -871,6 +908,8 @@ int sqlite3ExprResolveIds(
sqlite3VdbeAddOp(v, OP_PutStrKey, pExpr->iTable, 0);
}
}
sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO);
break;
}
@ -1002,8 +1041,9 @@ int sqlite3ExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
nErr = sqlite3ExprCheck(pParse, pExpr->pList->a[i].pExpr,
allowAgg && !is_agg, pIsAgg);
}
/** TODO: Compute pExpr->affinity based on the expected return
** type of the function */
/* FIX ME: Compute pExpr->affinity based on the expected return
** type of the function
*/
}
default: {
if( pExpr->pLeft ){
@ -1155,9 +1195,10 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
case TK_NE:
case TK_EQ: {
int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, 0);
CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight);
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3ExprCode(pParse, pExpr->pRight);
sqlite3VdbeAddOp(v, op, p1, 0);
sqlite3VdbeOp3(v, op, p1, 0, (void *)p3, P3_COLLSEQ);
break;
}
case TK_AND:
@ -1279,13 +1320,19 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
break;
}
case TK_BETWEEN: {
int p1;
CollSeq *p3;
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr);
sqlite3VdbeAddOp(v, OP_Ge, 0, 0);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, 0);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr);
sqlite3VdbeOp3(v, OP_Ge, p1, 0, (void *)p3, P3_COLLSEQ);
sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr);
sqlite3VdbeAddOp(v, OP_Le, 0, 0);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, 0);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr);
sqlite3VdbeOp3(v, OP_Le, p1, 0, (void *)p3, P3_COLLSEQ);
sqlite3VdbeAddOp(v, OP_And, 0, 0);
break;
}
@ -1312,8 +1359,11 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
for(i=0; i<nExpr; i=i+2){
sqlite3ExprCode(pParse, pExpr->pList->a[i].pExpr);
if( pExpr->pLeft ){
int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[i].pExpr, 1);
CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft,
pExpr->pList->a[i].pExpr);
sqlite3VdbeAddOp(v, OP_Dup, 1, 1);
jumpInst = sqlite3VdbeAddOp(v, OP_Ne, 1, 0);
jumpInst = sqlite3VdbeOp3(v, OP_Ne, p1, 0, (void *)p3, P3_COLLSEQ);
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
}else{
jumpInst = sqlite3VdbeAddOp(v, OP_IfNot, 1, 0);
@ -1426,9 +1476,10 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_NE:
case TK_EQ: {
int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, jumpIfNull);
CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight);
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3ExprCode(pParse, pExpr->pRight);
sqlite3VdbeAddOp(v, op, p1, dest);
sqlite3VdbeOp3(v, op, p1, dest, (void *)p3, P3_COLLSEQ);
break;
}
case TK_ISNULL:
@ -1438,13 +1489,27 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
break;
}
case TK_BETWEEN: {
/* The expression "x BETWEEN y AND z" is implemented as:
**
** 1 IF (x < y) GOTO 3
** 2 IF (x <= z) GOTO <dest>
** 3 ...
*/
int addr;
int p1;
CollSeq *p3;
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr);
addr = sqlite3VdbeAddOp(v, OP_Lt, !jumpIfNull, 0);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, !jumpIfNull);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr);
addr = sqlite3VdbeOp3(v, OP_Lt, p1, 0, (void *)p3, P3_COLLSEQ);
sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr);
sqlite3VdbeAddOp(v, OP_Le, jumpIfNull, dest);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, jumpIfNull);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr);
sqlite3VdbeOp3(v, OP_Le, p1, dest, (void *)p3, P3_COLLSEQ);
sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
@ -1505,9 +1570,10 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_NE:
case TK_EQ: {
int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, jumpIfNull);
CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight);
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3ExprCode(pParse, pExpr->pRight);
sqlite3VdbeAddOp(v, op, p1, dest);
sqlite3VdbeOp3(v, op, p1, dest, (void *)p3, P3_COLLSEQ);
break;
}
case TK_ISNULL:
@ -1516,33 +1582,29 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
sqlite3VdbeAddOp(v, op, 1, dest);
break;
}
#if 0
case TK_IN: {
int addr;
sqlite3ExprCode(pParse, pExpr->pLeft);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+3);
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
sqlite3VdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4);
if( pExpr->pSelect ){
sqlite3VdbeAddOp(v, OP_NotFound, pExpr->iTable, dest);
}else{
sqlite3VdbeAddOp(v, OP_SetNotFound, pExpr->iTable, dest);
}
break;
}
#endif
case TK_BETWEEN: {
/* The expression is "x BETWEEN y AND z". It is implemented as:
**
** 1 IF (x >= y) GOTO 3
** 2 GOTO <dest>
** 3 IF (x > z) GOTO <dest>
*/
int addr;
int p1;
CollSeq *p3;
sqlite3ExprCode(pParse, pExpr->pLeft);
sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp(v, OP_Ge, !jumpIfNull, addr+3);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, !jumpIfNull);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr);
sqlite3VdbeOp3(v, OP_Ge, p1, addr+3, (void *)p3, P3_COLLSEQ);
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
sqlite3VdbeAddOp(v, OP_Goto, 0, dest);
sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr);
sqlite3VdbeAddOp(v, OP_Gt, jumpIfNull, dest);
p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, jumpIfNull);
p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr);
sqlite3VdbeOp3(v, OP_Gt, p1, dest, (void *)p3, P3_COLLSEQ);
break;
}
default: {

View File

@ -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.207 2004/06/08 00:02:34 danielk1977 Exp $
** $Id: main.c,v 1.208 2004/06/09 09:55:18 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@ -424,6 +424,23 @@ static int binaryCollatingFunc(
return rc;
}
/*
** Another built-in collating sequence: NOCASE. At the moment there is
** only a UTF-8 implementation.
*/
static int nocaseCollatingFunc(
void *NotUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
int r = sqlite3StrNICmp(
(const char *)pKey1, (const char *)pKey2, (nKey1>nKey2)?nKey1:nKey2);
if( 0==r ){
r = nKey1-nKey2;
}
return r;
}
/*
** Return the ROWID of the most recent insert
*/
@ -1001,8 +1018,23 @@ static int openDatabase(
sqlite3HashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0);
sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1);
}
db->pDfltColl =
sqlite3ChangeCollatingFunction(db, "BINARY", 6, 0, binaryCollatingFunc);
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
** 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.
*/
sqlite3_create_collation(db, "BINARY", 0, 0, binaryCollatingFunc);
sqlite3_create_collation(db, "BINARY", 1, 0, binaryCollatingFunc);
db->pDfltColl = sqlite3FindCollSeq(db, "BINARY", 6, 0);
if( !db->pDfltColl ){
rc = db->errCode;
assert( rc!=SQLITE_OK );
db->magic = SQLITE_MAGIC_CLOSED;
goto opendb_out;
}
/* Also add a UTF-8 case-insensitive collation sequence. */
sqlite3_create_collation(db, "NOCASE", 0, 0, nocaseCollatingFunc);
/* Open the backend database driver */
if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
@ -1098,3 +1130,41 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0);
return rc;
}
int sqlite3_create_collation(
sqlite3* db,
const char *zName,
int pref16,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
CollSeq *pColl;
int rc = SQLITE_OK;
pColl = sqlite3FindCollSeq(db, zName, strlen(zName), 1);
if( 0==pColl ){
rc = SQLITE_NOMEM;
}else if( pref16 ){
pColl->xCmp16 = xCompare;
pColl->pUser16 = pCtx;
}else{
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
}
sqlite3Error(db, rc, 0);
return SQLITE_OK;
}
int sqlite3_create_collation16(
sqlite3* db,
const char *zName,
int pref16,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
int rc;
char *zName8 = sqlite3utf16to8(zName, -1, SQLITE_BIGENDIAN);
rc = sqlite3_create_collation(db, zName8, pref16, pCtx, xCompare);
sqliteFree(zName8);
return rc;
}

View File

@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.126 2004/06/09 00:48:13 drh Exp $
** @(#) $Id: parse.y,v 1.127 2004/06/09 09:55:18 danielk1977 Exp $
*/
%token_prefix TK_
%token_type {Token}
@ -423,7 +423,7 @@ on_opt(N) ::= . {N = 0;}
%type using_opt {IdList*}
%destructor using_opt {sqlite3IdListDelete($$);}
using_opt(U) ::= USING LP idxlist(L) RP. {U = L;}
using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
using_opt(U) ::= . {U = 0;}
@ -741,17 +741,32 @@ cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) dbnm(D)
uniqueflag(A) ::= UNIQUE. { A = OE_Abort; }
uniqueflag(A) ::= . { A = OE_None; }
%type idxlist {IdList*}
%destructor idxlist {sqlite3IdListDelete($$);}
%type idxlist_opt {IdList*}
%destructor idxlist_opt {sqlite3IdListDelete($$);}
%type idxlist {ExprList*}
%destructor idxlist {sqlite3ExprListDelete($$);}
%type idxlist_opt {ExprList*}
%destructor idxlist_opt {sqlite3ExprListDelete($$);}
%type idxitem {Token}
idxlist_opt(A) ::= . {A = 0;}
idxlist_opt(A) ::= LP idxlist(X) RP. {A = X;}
idxlist(A) ::= idxlist(X) COMMA idxitem(Y). {A = sqlite3IdListAppend(X,&Y);}
idxlist(A) ::= idxitem(Y). {A = sqlite3IdListAppend(0,&Y);}
idxitem(A) ::= nm(X) sortorder. {A = X;}
idxlist(A) ::= idxlist(X) COMMA idxitem(Y) collate(C) sortorder. {
Expr *p = 0;
if( C.n>0 ){
p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
if( p ) p->pColl = sqlite3LocateCollSeq(pParse, C.z, C.n);
}
A = sqlite3ExprListAppend(X, p, &Y);
}
idxlist(A) ::= idxitem(Y) collate(C) sortorder. {
Expr *p = 0;
if( C.n>0 ){
p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
if( p ) p->pColl = sqlite3LocateCollSeq(pParse, C.z, C.n);
}
A = sqlite3ExprListAppend(0, p, &Y);
}
idxitem(A) ::= nm(X). {A = X;}
///////////////////////////// The DROP INDEX command /////////////////////////
//

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.184 2004/06/07 10:00:31 danielk1977 Exp $
** $Id: select.c,v 1.185 2004/06/09 09:55:18 danielk1977 Exp $
*/
#include "sqliteInt.h"
@ -546,7 +546,14 @@ static void generateSortTail(
pInfo->aSortOrder = (char*)&pInfo->aColl[nCol];
pInfo->nField = nCol;
for(i=0; i<nCol; i++){
/* If a collation sequence was specified explicity, then it
** is stored in pOrderBy->a[i].zName. Otherwise, use the default
** collation type for the expression.
*/
pInfo->aColl[i] = sqlite3ExprCollSeq(pOrderBy->a[i].pExpr);
if( !pInfo->aColl[i] ){
pInfo->aColl[i] = db->pDfltColl;
}
pInfo->aSortOrder[i] = pOrderBy->a[i].sortOrder;
}
sqlite3VdbeOp3(v, OP_Sort, 0, 0, (char*)pInfo, P3_KEYINFO_HANDOFF);
@ -818,6 +825,10 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
if( zType ){
pTab->aCol[i].affinity = sqlite3AffinityType(zType, strlen(zType));
}
pTab->aCol[i].pColl = sqlite3ExprCollSeq(p);
if( !pTab->aCol[i].pColl ){
pTab->aCol[i].pColl = pParse->db->pDfltColl;
}
}
pTab->iPKey = -1;
return pTab;
@ -2222,6 +2233,21 @@ int sqlite3Select(
}
}
/* If there is an ORDER BY clause, resolve any collation sequences
** names that have been explicitly specified.
*/
if( pOrderBy ){
for(i=0; i<pOrderBy->nExpr; i++){
if( pOrderBy->a[i].zName ){
pOrderBy->a[i].pExpr->pColl =
sqlite3LocateCollSeq(pParse, pOrderBy->a[i].zName, -1);
}
}
if( pParse->nErr ){
goto select_end;
}
}
/* Begin generating code.
*/
v = sqlite3GetVdbe(pParse);

View File

@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.94 2004/06/08 00:02:35 danielk1977 Exp $
** @(#) $Id: sqlite.h.in,v 1.95 2004/06/09 09:55:18 danielk1977 Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
@ -963,6 +963,22 @@ void sqlite3_result_text(sqlite3_context*, const char*, int n, int eCopy);
void sqlite3_result_text16(sqlite3_context*, const void*, int n, int eCopy);
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
int sqlite3_create_collation(
sqlite3*,
const char *zName,
int pref16,
void*,
int(*xCompare)(void*,int,const void*,int,const void*)
);
int sqlite3_create_collation16(
sqlite3*,
const char *zName,
int pref16,
void*,
int(*xCompare)(void*,int,const void*,int,const void*)
);
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif

View File

@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.272 2004/06/09 00:48:13 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.273 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "config.h"
#include "sqlite3.h"
@ -488,19 +488,31 @@ struct Column {
/*
** A "Collating Sequence" is defined by an instance of the following
** structure. Every collating sequence has a name and a comparison
** function that defines the order of text for that sequence. The
** CollSeq.pUser parameter is an extra parameter that passed in as
** the first argument to the comparison function.
** structure. Conceptually, a collating sequence consists of a name and
** a comparison routine that defines the order of that sequence.
**
** If CollSeq.xCmp is NULL, it means that the collating sequence is
** undefined. Indices built on an undefined collating sequence may
** not be read or written.
** There may two seperate implementations of the collation function, one
** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that
** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine
** native byte order. When a collation sequence is invoked, SQLite selects
** the version that will require the least expensive encoding
** transalations, if any.
**
** The CollSeq.pUser member variable is an extra parameter that passed in
** as the first argument to the UTF-8 comparison function, xCmp.
** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function,
** xCmp16.
**
** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the
** collating sequence is undefined. Indices built on an undefined
** collating sequence may not be read or written.
*/
struct CollSeq {
char *zName; /* Name of the collating sequence */
char *zName; /* Name of the collating sequence, UTF-8 encoded */
void *pUser; /* First argument to xCmp() */
int (*xCmp)(void*,int,const void*,int,const void*); /* Comparison function */
void *pUser16; /* First argument to xCmp16() */
int (*xCmp)(void*,int, const void*, int, const void*);
int (*xCmp16)(void*,int, const void*, int, const void*);
};
/*
@ -756,6 +768,7 @@ struct Token {
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
CollSeq *pColl; /* The collation type of the column or 0 */
u8 iDb; /* Database referenced by this expression */
u8 flags; /* Various flags. See below */
Expr *pLeft, *pRight; /* Left and right subnodes */
@ -1224,12 +1237,10 @@ void sqlite3OpenMasterTable(Vdbe *v, int);
void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int);
void sqlite3AddColumn(Parse*,Token*);
void sqlite3AddNotNull(Parse*, int);
void sqlite3AddPrimaryKey(Parse*, IdList*, int);
void sqlite3AddPrimaryKey(Parse*, ExprList*, int);
void sqlite3AddColumnType(Parse*,Token*,Token*);
void sqlite3AddDefaultValue(Parse*,Token*,int);
void sqlite3AddCollateType(Parse*, const char*, int);
CollSeq *sqlite3ChangeCollatingFunction(sqlite*,const char*,int,
void*, int(*)(void*,int,const void*,int,const void*));
void sqlite3EndTable(Parse*,Token*,Select*);
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int);
int sqlite3ViewGetColumnNames(Parse*,Table*);
@ -1243,7 +1254,7 @@ void sqlite3SrcListAddAlias(SrcList*, Token*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(IdList*);
void sqlite3SrcListDelete(SrcList*);
void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,IdList*,int,Token*,
void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Token*);
void sqlite3DropIndex(Parse*, SrcList*);
void sqlite3AddKeyType(Vdbe*, ExprList*);
@ -1324,7 +1335,7 @@ TriggerStep *sqlite3TriggerUpdateStep(Token*, ExprList*, Expr*, int);
TriggerStep *sqlite3TriggerDeleteStep(Token*, Expr*);
void sqlite3DeleteTrigger(Trigger*);
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
void sqlite3CreateForeignKey(Parse*, IdList*, Token*, IdList*, int);
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
void sqlite3DeferForeignKey(Parse*, int);
#ifndef SQLITE_OMIT_AUTHORIZATION
void sqlite3AuthRead(Parse*,Expr*,SrcList*);
@ -1379,3 +1390,6 @@ int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
const char *sqlite3ErrStr(int);
int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold);
int sqlite3ReadSchema(sqlite *db);
CollSeq *sqlite3FindCollSeq(sqlite *,const char *,int,int);
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName);
CollSeq *sqlite3ExprCollSeq(Expr *pExpr);

View File

@ -11,7 +11,7 @@
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.80 2004/06/08 00:02:35 danielk1977 Exp $
** $Id: tclsqlite.c,v 1.81 2004/06/09 09:55:19 danielk1977 Exp $
*/
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@ -42,6 +42,17 @@ struct SqlFunc {
SqlFunc *pNext; /* Next function on the list of them all */
};
/*
** New collation sequences function can be created as TCL scripts. Each such
** function is described by an instance of the following structure.
*/
typedef struct SqlCollate SqlCollate;
struct SqlCollate {
Tcl_Interp *interp; /* The TCL interpret to execute the function */
char *zScript; /* The script to be run */
SqlCollate *pNext; /* Next function on the list of them all */
};
/*
** There is one instance of this structure for each SQLite database
** that has been opened by the SQLite TCL interface.
@ -56,6 +67,7 @@ struct SqliteDb {
char *zProgress; /* The progress callback routine */
char *zAuth; /* The authorization callback routine */
SqlFunc *pFunc; /* List of SQL functions */
SqlCollate *pCollate; /* List of SQL collation functions */
int rc; /* Return code of most recent sqlite3_exec() */
int nChange; /* Database changes for the most recent eval */
};
@ -115,6 +127,11 @@ static void DbDeleteCmd(void *db){
pDb->pFunc = pFunc->pNext;
Tcl_Free((char*)pFunc);
}
while( pDb->pCollate ){
SqlCollate *pCollate = pDb->pCollate;
pDb->pCollate = pCollate->pNext;
Tcl_Free((char*)pCollate);
}
if( pDb->zBusy ){
Tcl_Free(pDb->zBusy);
}
@ -200,6 +217,29 @@ static int DbCommitHandler(void *cd){
return 0;
}
/*
** This routine is called to evaluate an SQL collation function implemented
** using TCL script.
*/
static int tclSqlCollate(
void *pCtx,
int nA,
const void *zA,
int nB,
const void *zB
){
SqlCollate *p = (SqlCollate *)pCtx;
Tcl_Obj *pCmd;
pCmd = Tcl_NewStringObj(p->zScript, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA));
Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB));
Tcl_EvalObjEx(p->interp, pCmd, 0);
Tcl_DecrRefCount(pCmd);
return (atoi(Tcl_GetStringResult(p->interp)));
}
/*
** This routine is called to evaluate an SQL function implemented
** using TCL script.
@ -342,7 +382,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
"errorcode", "eval", "function",
"last_insert_rowid", "last_statement_changes", "onecolumn",
"progress", "rekey", "timeout",
"trace",
"trace", "collate",
0
};
enum DB_enum {
@ -351,7 +391,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
DB_ERRORCODE, DB_EVAL, DB_FUNCTION,
DB_LAST_INSERT_ROWID, DB_LAST_STATEMENT_CHANGES, DB_ONECOLUMN,
DB_PROGRESS, DB_REKEY, DB_TIMEOUT,
DB_TRACE
DB_TRACE, DB_COLLATE
};
if( objc<2 ){
@ -854,6 +894,35 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
}
/*
** $db collate NAME SCRIPT
**
** Create a new SQL collation function called NAME. Whenever
** that function is called, invoke SCRIPT to evaluate the function.
*/
case DB_COLLATE: {
SqlCollate *pCollate;
char *zName;
char *zScript;
int nScript;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
return TCL_ERROR;
}
zName = Tcl_GetStringFromObj(objv[2], 0);
zScript = Tcl_GetStringFromObj(objv[3], &nScript);
pCollate = (SqlCollate*)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 );
if( pCollate==0 ) return TCL_ERROR;
pCollate->interp = interp;
pCollate->pNext = pDb->pCollate;
pCollate->zScript = (char*)&pCollate[1];
strcpy(pCollate->zScript, zScript);
if( sqlite3_create_collation(pDb->db, zName, 0, pCollate, tclSqlCollate) ){
return TCL_ERROR;
}
break;
}
} /* End of the SWITCH statement */
return rc;
}

View File

@ -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.71 2004/06/08 00:02:35 danielk1977 Exp $
** $Id: test1.c,v 1.72 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@ -1550,47 +1550,6 @@ static int test_data_count(
return TCL_OK;
}
/*
** This is a collating function named "REVERSE" which sorts text
** in reverse order.
*/
static int reverseCollatingFunc(
void *NotUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
int rc, n;
n = nKey1<nKey2 ? nKey1 : nKey2;
rc = memcmp(pKey1, pKey2, n);
if( rc==0 ){
rc = nKey1 - nKey2;
}
return -rc;
}
/*
** Usage: add_reverse_collating_func DB
**
** This routine adds a collation named "REVERSE" to database given.
** REVERSE is used for testing only.
*/
static int reverse_collfunc(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
sqlite3ChangeCollatingFunction(db, "REVERSE", 7, 0, reverseCollatingFunc);
return TCL_OK;
}
/*
** Usage: sqlite3_column_text STMT column
**
@ -1880,7 +1839,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_errmsg16", test_errmsg16 ,0 },
{ "sqlite3_open", test_open ,0 },
{ "sqlite3_open16", test_open16 ,0 },
{ "add_reverse_collating_func", reverse_collfunc ,0 },
{ "sqlite3_prepare", test_prepare ,0 },
{ "sqlite3_prepare16", test_prepare16 ,0 },

View File

@ -15,9 +15,10 @@
** is used for testing the SQLite routines for converting between
** the various supported unicode encodings.
**
** $Id: test5.c,v 1.8 2004/06/04 06:22:02 danielk1977 Exp $
** $Id: test5.c,v 1.9 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
#include "os.h" /* to get SQLITE_BIGENDIAN */
#include "tcl.h"
#include <stdlib.h>
@ -234,6 +235,52 @@ static int binarize(
return TCL_OK;
}
/*
** Usage: test_value_overhead <repeat-count> <do-calls>.
**
** This routine is used to test the overhead of calls to
** sqlite3_value_text(), on a value that contains a UTF-8 string. The idea
** is to figure out whether or not it is a problem to use sqlite3_value
** structures with collation sequence functions.
**
** If <do-calls> is 0, then the calls to sqlite3_value_text() are not
** actually made.
*/
static int test_value_overhead(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
int do_calls;
int repeat_count;
int i;
Mem val;
const char *zVal;
if( objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " <repeat-count> <do-calls>", 0);
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[1], &repeat_count) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &do_calls) ) return TCL_ERROR;
val.flags = MEM_Str|MEM_Term|MEM_Static;
val.z = "hello world";
val.type = SQLITE_TEXT;
val.enc = TEXT_Utf8;
for(i=0; i<repeat_count; i++){
if( do_calls ){
zVal = sqlite3_value_text(&val);
}
}
return TCL_OK;
}
/*
** Register commands with the TCL interpreter.
@ -249,11 +296,12 @@ int Sqlitetest5_Init(Tcl_Interp *interp){
{ "sqlite_utf16to16le", (Tcl_ObjCmdProc*)sqlite_utf16to16le },
{ "sqlite_utf16to16be", (Tcl_ObjCmdProc*)sqlite_utf16to16be },
{ "binarize", (Tcl_ObjCmdProc*)binarize },
{ "test_value_overhead", (Tcl_ObjCmdProc*)test_value_overhead },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
}
return TCL_OK;
return SQLITE_OK;
}

View File

@ -14,7 +14,7 @@
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.98 2004/06/06 12:41:50 danielk1977 Exp $
** $Id: util.c,v 1.99 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
@ -553,7 +553,7 @@ int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
return N<0 ? 0 : *a - *b;
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
}
/*

View File

@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.360 2004/06/09 00:48:14 drh Exp $
** $Id: vdbe.c,v 1.361 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@ -1510,25 +1510,25 @@ mismatch:
/* Opcode: Lt P1 P2 P3
**
** This works just like the Eq opcode except that the jump is taken if
** the 2nd element down on the task is less than the top of the stack.
** the 2nd element down on the stack is less than the top of the stack.
** See the Eq opcode for additional information.
*/
/* Opcode: Le P1 P2 P3
**
** This works just like the Eq opcode except that the jump is taken if
** the 2nd element down on the task is less than or equal to the
** the 2nd element down on the stack is less than or equal to the
** top of the stack. See the Eq opcode for additional information.
*/
/* Opcode: Gt P1 P2 P3
**
** This works just like the Eq opcode except that the jump is taken if
** the 2nd element down on the task is greater than the top of the stack.
** the 2nd element down on the stack is greater than the top of the stack.
** See the Eq opcode for additional information.
*/
/* Opcode: Ge P1 P2 P3
**
** This works just like the Eq opcode except that the jump is taken if
** the 2nd element down on the task is greater than or equal to the
** the 2nd element down on the stack is greater than or equal to the
** top of the stack. See the Eq opcode for additional information.
*/
case OP_Eq:

View File

@ -1256,7 +1256,7 @@ void sqlite3VdbeDelete(Vdbe *p){
int j;
VdbeFunc *pVdbeFunc = (VdbeFunc *)pOp->p3;
for(j=0; j<pVdbeFunc->nAux; j++){
struct AuxData *pAuxData = &pVdbeFunc->apAux[j].pAux;
struct AuxData *pAuxData = &pVdbeFunc->apAux[j];
if( pAuxData->pAux && pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}
@ -1521,14 +1521,17 @@ int sqlite3VdbeRecordCompare(
const unsigned char *aKey1 = (const unsigned char *)pKey1;
const unsigned char *aKey2 = (const unsigned char *)pKey2;
Mem mem1;
Mem mem2;
mem1.enc = pKeyInfo->enc;
mem2.enc = pKeyInfo->enc;
idx1 = sqlite3GetVarint32(pKey1, &szHdr1);
d1 = szHdr1;
idx2 = sqlite3GetVarint32(pKey2, &szHdr2);
d2 = szHdr2;
nField = pKeyInfo->nField;
while( idx1<szHdr1 && idx2<szHdr2 ){
Mem mem1;
Mem mem2;
u32 serial_type1;
u32 serial_type2;

View File

@ -427,13 +427,42 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
if( (f2 & MEM_Str)==0 ){
return -1;
}
if( pColl && pColl->xCmp ){
return pColl->xCmp(pColl->pUser, pMem1->n, pMem1->z, pMem2->n, pMem2->z);
assert( pMem1->enc==pMem2->enc );
assert( pMem1->enc==TEXT_Utf8 ||
pMem1->enc==TEXT_Utf16le || pMem1->enc==TEXT_Utf16be );
/* FIX ME: This may fail if the collation sequence is deleted after
** this vdbe program is compiled. We cannot just use BINARY in this
** case as this may lead to a segfault caused by traversing an index
** table incorrectly. We need to return an error to the user in this
** case.
*/
assert( !pColl || (pColl->xCmp || pColl->xCmp16) );
if( pColl ){
if( (pMem1->enc==TEXT_Utf8 && pColl->xCmp) || !pColl->xCmp16 ){
return pColl->xCmp(
pColl->pUser,
sqlite3_value_bytes((sqlite3_value *)pMem1),
sqlite3_value_text((sqlite3_value *)pMem1),
sqlite3_value_bytes((sqlite3_value *)pMem2),
sqlite3_value_text((sqlite3_value *)pMem2)
);
}else{
/* If no collating sequence is defined, fall through into the
** blob case below and use memcmp() for the comparison. */
return pColl->xCmp16(
pColl->pUser,
sqlite3_value_bytes16((sqlite3_value *)pMem1),
sqlite3_value_text16((sqlite3_value *)pMem1),
sqlite3_value_bytes16((sqlite3_value *)pMem2),
sqlite3_value_text16((sqlite3_value *)pMem2)
);
}
}
/* If a NULL pointer was passed as the collate function, fall through
** to the blob case and use memcmp().
*/
}
/* Both values must be blobs. Compare using memcmp().
*/

View File

@ -12,7 +12,7 @@
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements.
**
** $Id: where.c,v 1.102 2004/06/09 00:48:15 drh Exp $
** $Id: where.c,v 1.103 2004/06/09 09:55:20 danielk1977 Exp $
*/
#include "sqliteInt.h"
@ -208,6 +208,7 @@ static void exprAnalyze(ExprMaskSet *pMaskSet, ExprInfo *pInfo){
** set to 0 if the ORDER BY clause is all ASC.
*/
static Index *findSortingIndex(
sqlite *db,
Table *pTab, /* The table to be sorted */
int base, /* Cursor number for pTab */
ExprList *pOrderBy, /* The ORDER BY clause */
@ -230,10 +231,6 @@ static Index *findSortingIndex(
** DESC or ASC. Indices cannot be used on a mixture. */
return 0;
}
if( pOrderBy->a[i].zName!=0 ){
/* Do not sort by index if there is a COLLATE clause */
return 0;
}
p = pOrderBy->a[i].pExpr;
if( p->op!=TK_COLUMN || p->iTable!=base ){
/* Can not use an index sort on anything that is not a column in the
@ -251,12 +248,23 @@ static Index *findSortingIndex(
int nExpr = pOrderBy->nExpr;
if( pIdx->nColumn < nEqCol || pIdx->nColumn < nExpr ) continue;
for(i=j=0; i<nEqCol; i++){
CollSeq *pColl = sqlite3ExprCollSeq(pOrderBy->a[j].pExpr);
if( !pColl ) pColl = db->pDfltColl;
if( pPreferredIdx->aiColumn[i]!=pIdx->aiColumn[i] ) break;
if( j<nExpr && pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] ){ j++; }
if( pPreferredIdx->keyInfo.aColl[i]!=pIdx->keyInfo.aColl[i] ) break;
if( j<nExpr &&
pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] &&
pColl==pIdx->keyInfo.aColl[i]
){
j++;
}
}
if( i<nEqCol ) continue;
for(i=0; i+j<nExpr; i++){
if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] ) break;
CollSeq *pColl = sqlite3ExprCollSeq(pOrderBy->a[i+j].pExpr);
if( !pColl ) pColl = db->pDfltColl;
if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] ||
pColl!=pIdx->keyInfo.aColl[i+nEqCol] ) break;
}
if( i+j>=nExpr ){
pMatch = pIdx;
@ -532,14 +540,24 @@ WhereInfo *sqlite3WhereBegin(
if( pIdx->nColumn>32 ) continue; /* Ignore indices too many columns */
for(j=0; j<nExpr; j++){
CollSeq *pColl = sqlite3ExprCollSeq(aExpr[j].p->pLeft);
if( !pColl && aExpr[j].p->pRight ){
pColl = sqlite3ExprCollSeq(aExpr[j].p->pRight);
}
if( !pColl ){
pColl = pParse->db->pDfltColl;
}
if( aExpr[j].idxLeft==iCur
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
int iColumn = aExpr[j].p->pLeft->iColumn;
int k;
char idxaff = pIdx->pTable->aCol[iColumn].affinity;
for(k=0; k<pIdx->nColumn; k++){
if( pIdx->aiColumn[k]==iColumn
&& sqlite3IndexAffinityOk(aExpr[j].p, idxaff) ){
/* If the collating sequences or affinities don't match,
** ignore this index. */
if( pColl!=pIdx->keyInfo.aColl[k] ) continue;
if( !sqlite3IndexAffinityOk(aExpr[j].p, idxaff) ) continue;
if( pIdx->aiColumn[k]==iColumn ){
switch( aExpr[j].p->op ){
case TK_IN: {
if( k==0 ) inMask |= 1;
@ -575,8 +593,11 @@ WhereInfo *sqlite3WhereBegin(
int k;
char idxaff = pIdx->pTable->aCol[iColumn].affinity;
for(k=0; k<pIdx->nColumn; k++){
if( pIdx->aiColumn[k]==iColumn
&& sqlite3IndexAffinityOk(aExpr[j].p, idxaff) ){
/* If the collating sequences or affinities don't match,
** ignore this index. */
if( pColl!=pIdx->keyInfo.aColl[k] ) continue;
if( !sqlite3IndexAffinityOk(aExpr[j].p, idxaff) ) continue;
if( pIdx->aiColumn[k]==iColumn ){
switch( aExpr[j].p->op ){
case TK_EQ: {
eqMask |= 1<<k;
@ -655,7 +676,7 @@ WhereInfo *sqlite3WhereBegin(
pSortIdx = 0;
}else{
int nEqCol = (pWInfo->a[0].score+4)/8;
pSortIdx = findSortingIndex(pTab, pTabList->a[0].iCursor,
pSortIdx = findSortingIndex(pParse->db, pTab, pTabList->a[0].iCursor,
*ppOrderBy, pIdx, nEqCol, &bRev);
}
if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ){

226
test/collate1.test Normal file
View File

@ -0,0 +1,226 @@
#
# The author or author's hereby grant to the public domain a non-exclusive,
# fully paid-up, perpetual, license in the software and all related
# intellectual property to make, have made, use, have used, reproduce,
# prepare derivative works, distribute, perform and display the work.
#
#*************************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing the ORDER BY clause with
# user-defined collation sequences.
#
# $Id: collate1.test,v 1.1 2004/06/09 09:55:20 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
#
# Tests are roughly organised as follows:
#
# collate1-1.* - Single-field ORDER BY with an explicit COLLATE clause.
# collate1-2.* - Multi-field ORDER BY with an explicit COLLATE clause.
# collate1-3.* - ORDER BY using a default collation type. Also that an
# explict collate type overrides a default collate type.
# collate1-4.* - ORDER BY using a data type.
#
#
# Collation type 'HEX'. If an argument can be interpreted as a hexadecimal
# number, then it is converted to one before the comparison is performed.
# Numbers are less than other strings. If neither argument is a number,
# [string compare] is used.
#
db collate HEX hex_collate
proc hex_collate {lhs rhs} {
set lhs_ishex [regexp {^(0x|)[1234567890abcdefABCDEF]+$} $lhs]
set rhs_ishex [regexp {^(0x|)[1234567890abcdefABCDEF]+$} $rhs]
if {$lhs_ishex && $rhs_ishex} {
set lhsx [scan $lhs %x]
set rhsx [scan $rhs %x]
if {$lhs < $rhs} {return -1}
if {$lhs == $rhs} {return 0}
if {$lhs > $rhs} {return 1}
}
if {$lhs_ishex} {
return -1;
}
if {$rhs_ishex} {
return 1;
}
return [string compare $lhs $rhs]
}
db function hex {format 0x%X}
# Mimic the SQLite 2 collation type NUMERIC.
db collate numeric numeric_collate
proc numeric_collate {lhs rhs} {
if {$lhs == $rhs} {return 0}
return [expr ($lhs>$rhs)?1:-1]
}
do_test collate1-1.0 {
execsql {
CREATE TABLE collate1t1(c1, c2);
INSERT INTO collate1t1 VALUES(45, hex(45));
INSERT INTO collate1t1 VALUES(NULL, NULL);
INSERT INTO collate1t1 VALUES(281, hex(281));
}
} {}
do_test collate1-1.1 {
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1;
}
} {{} 0x119 0x2D}
do_test collate1-1.2 {
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex;
}
} {{} 0x2D 0x119}
do_test collate1-1.3 {
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex DESC;
}
} {0x119 0x2D {}}
do_test collate1-1.4 {
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex ASC;
}
} {{} 0x2D 0x119}
do_test collate1-1.5 {
execsql {
DROP TABLE collate1t1;
}
} {}
do_test collate1-2.0 {
execsql {
CREATE TABLE collate1t1(c1, c2);
INSERT INTO collate1t1 VALUES('5', '0x11');
INSERT INTO collate1t1 VALUES('5', '0xA');
INSERT INTO collate1t1 VALUES(NULL, NULL);
INSERT INTO collate1t1 VALUES('7', '0xA');
INSERT INTO collate1t1 VALUES('11', '0x11');
INSERT INTO collate1t1 VALUES('11', '0x101');
}
} {}
do_test collate1-2.2 {
execsql {
SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE numeric, 2 COLLATE hex;
}
} {{} {} 5 0xA 5 0x11 7 0xA 11 0x11 11 0x101}
do_test collate1-2.3 {
execsql {
SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE binary, 2 COLLATE hex;
}
} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
do_test collate1-2.4 {
execsql {
SELECT c1, c2 FROM collate1t1 ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex;
}
} {7 0xA 5 0xA 5 0x11 11 0x11 11 0x101 {} {}}
do_test collate1-2.5 {
execsql {
SELECT c1, c2 FROM collate1t1
ORDER BY 1 COLLATE binary DESC, 2 COLLATE hex DESC;
}
} {7 0xA 5 0x11 5 0xA 11 0x101 11 0x11 {} {}}
do_test collate1-2.6 {
execsql {
SELECT c1, c2 FROM collate1t1
ORDER BY 1 COLLATE binary ASC, 2 COLLATE hex ASC;
}
} {{} {} 11 0x11 11 0x101 5 0xA 5 0x11 7 0xA}
do_test collate1-2.7 {
execsql {
DROP TABLE collate1t1;
}
} {}
#
# These tests ensure that the default collation type for a column is used
# by an ORDER BY clause correctly. The focus is all the different ways
# the column can be referenced. i.e. a, collate2t1.a, main.collate2t1.a etc.
#
do_test collate1-3.0 {
execsql {
CREATE TABLE collate1t1(a COLLATE hex, b);
INSERT INTO collate1t1 VALUES( '0x5', 5 );
INSERT INTO collate1t1 VALUES( '1', 1 );
INSERT INTO collate1t1 VALUES( '0x45', 69 );
INSERT INTO collate1t1 VALUES( NULL, NULL );
SELECT * FROM collate1t1 ORDER BY a;
}
} {{} {} 1 1 0x5 5 0x45 69}
do_test collate1-3.1 {
execsql {
SELECT * FROM collate1t1 ORDER BY 1;
}
} {{} {} 1 1 0x5 5 0x45 69}
do_test collate1-3.2 {
execsql {
SELECT * FROM collate1t1 ORDER BY collate1t1.a;
}
} {{} {} 1 1 0x5 5 0x45 69}
do_test collate1-3.3 {
execsql {
SELECT * FROM collate1t1 ORDER BY main.collate1t1.a;
}
} {{} {} 1 1 0x5 5 0x45 69}
do_test collate1-3.4 {
execsql {
SELECT a as c1, b as c2 FROM collate1t1 ORDER BY c1;
}
} {{} {} 1 1 0x5 5 0x45 69}
do_test collate1-3.5 {
execsql {
SELECT a as c1, b as c2 FROM collate1t1 ORDER BY c1 COLLATE binary;
}
} {{} {} 0x45 69 0x5 5 1 1}
do_test collate1-3.6 {
execsql {
DROP TABLE collate1t1;
}
} {}
# Update for SQLite version 3. The collate1-4.* test cases were written
# before manifest types were introduced. The following test cases still
# work, due to the 'affinity' mechanism, but they don't prove anything
# about collation sequences.
#
do_test collate1-4.0 {
execsql {
CREATE TABLE collate1t1(c1 numeric, c2 text);
INSERT INTO collate1t1 VALUES(1, 1);
INSERT INTO collate1t1 VALUES(12, 12);
INSERT INTO collate1t1 VALUES(NULL, NULL);
INSERT INTO collate1t1 VALUES(101, 101);
}
} {}
do_test collate1-4.1 {
execsql {
SELECT c1 FROM collate1t1 ORDER BY 1;
}
} {{} 1 12 101}
do_test collate1-4.2 {
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1;
}
} {{} 1 101 12}
do_test collate1-4.3 {
execsql {
SELECT c2+0 FROM collate1t1 ORDER BY 1;
}
} {{} 1 12 101}
do_test collate1-4.4 {
execsql {
SELECT c1||'' FROM collate1t1 ORDER BY 1;
}
} {{} 1 101 12}
do_test collate1-4.5 {
execsql {
DROP TABLE collate1t1;
}
} {}
finish_test

604
test/collate2.test Normal file
View File

@ -0,0 +1,604 @@
#
# The author or author's hereby grant to the public domain a non-exclusive,
# fully paid-up, perpetual, license in the software and all related
# intellectual property to make, have made, use, have used, reproduce,
# prepare derivative works, distribute, perform and display the work.
#
#*************************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing comparison operators in expressions
# that use user-defined collation sequences.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
#
# Tests are organised as follows:
#
# collate2-1.* WHERE <expr> expressions (sqliteExprIfTrue).
# collate2-2.* WHERE NOT <expr> expressions (sqliteExprIfFalse).
# collate2-3.* SELECT <expr> expressions (sqliteExprCode).
# collate2-4.* Precedence of collation/data types in binary comparisons
# collate2-5.* JOIN syntax.
#
# Create a collation type BACKWARDS for use in testing. This collation type
# is similar to the built-in TEXT collation type except the order of
# characters in each string is reversed before the comparison is performed.
db collate BACKWARDS backwards_collate
proc backwards_collate {a b} {
set ra {};
set rb {}
foreach c [split $a {}] { set ra $c$ra }
foreach c [split $b {}] { set rb $c$rb }
return [string compare $ra $rb]
}
# The following values are used in these tests:
# NULL aa ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB
#
# The collation orders for each of the tested collation types are:
#
# BINARY: NULL AA AB Aa Ab BA BB Ba Bb aA aB aa ab bA bB ba bb
# NOCASE: NULL aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB
# BACKWARDS: NULL AA BA aA bA AB BB aB bB Aa Ba aa ba Ab Bb ab bb
#
# These tests verify that the default collation type for a column is used
# for comparison operators (<, >, <=, >=, =) involving that column and
# an expression that is not a column with a default collation type.
#
# The collation sequences BINARY and NOCASE are built-in, the BACKWARDS
# collation sequence is implemented by the TCL proc backwards_collate
# above.
#
do_test collate2-1.0 {
execsql {
CREATE TABLE collate2t1(
a COLLATE BINARY,
b COLLATE NOCASE,
c COLLATE BACKWARDS
);
INSERT INTO collate2t1 VALUES( NULL, NULL, NULL );
INSERT INTO collate2t1 VALUES( 'aa', 'aa', 'aa' );
INSERT INTO collate2t1 VALUES( 'ab', 'ab', 'ab' );
INSERT INTO collate2t1 VALUES( 'ba', 'ba', 'ba' );
INSERT INTO collate2t1 VALUES( 'bb', 'bb', 'bb' );
INSERT INTO collate2t1 VALUES( 'aA', 'aA', 'aA' );
INSERT INTO collate2t1 VALUES( 'aB', 'aB', 'aB' );
INSERT INTO collate2t1 VALUES( 'bA', 'bA', 'bA' );
INSERT INTO collate2t1 VALUES( 'bB', 'bB', 'bB' );
INSERT INTO collate2t1 VALUES( 'Aa', 'Aa', 'Aa' );
INSERT INTO collate2t1 VALUES( 'Ab', 'Ab', 'Ab' );
INSERT INTO collate2t1 VALUES( 'Ba', 'Ba', 'Ba' );
INSERT INTO collate2t1 VALUES( 'Bb', 'Bb', 'Bb' );
INSERT INTO collate2t1 VALUES( 'AA', 'AA', 'AA' );
INSERT INTO collate2t1 VALUES( 'AB', 'AB', 'AB' );
INSERT INTO collate2t1 VALUES( 'BA', 'BA', 'BA' );
INSERT INTO collate2t1 VALUES( 'BB', 'BB', 'BB' );
}
if {[info exists collate_test_use_index]} {
execsql {
CREATE INDEX collate2t1_i1 ON collate2t1(a);
CREATE INDEX collate2t1_i2 ON collate2t1(b);
CREATE INDEX collate2t1_i3 ON collate2t1(c);
}
}
} {}
do_test collate2-1.1 {
execsql {
SELECT a FROM collate2t1 WHERE a > 'aa' ORDER BY 1;
}
} {ab bA bB ba bb}
do_test collate2-1.2 {
execsql {
SELECT b FROM collate2t1 WHERE b > 'aa' ORDER BY 1, oid;
}
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
do_test collate2-1.3 {
execsql {
SELECT c FROM collate2t1 WHERE c > 'aa' ORDER BY 1;
}
} {ba Ab Bb ab bb}
do_test collate2-1.4 {
execsql {
SELECT a FROM collate2t1 WHERE a < 'aa' ORDER BY 1;
}
} {AA AB Aa Ab BA BB Ba Bb aA aB}
do_test collate2-1.5 {
execsql {
SELECT b FROM collate2t1 WHERE b < 'aa' ORDER BY 1, oid;
}
} {}
do_test collate2-1.6 {
execsql {
SELECT c FROM collate2t1 WHERE c < 'aa' ORDER BY 1;
}
} {AA BA aA bA AB BB aB bB Aa Ba}
do_test collate2-1.7 {
execsql {
SELECT a FROM collate2t1 WHERE a = 'aa';
}
} {aa}
do_test collate2-1.8 {
execsql {
SELECT b FROM collate2t1 WHERE b = 'aa' ORDER BY oid;
}
} {aa aA Aa AA}
do_test collate2-1.9 {
execsql {
SELECT c FROM collate2t1 WHERE c = 'aa';
}
} {aa}
do_test collate2-1.10 {
execsql {
SELECT a FROM collate2t1 WHERE a >= 'aa' ORDER BY 1;
}
} {aa ab bA bB ba bb}
do_test collate2-1.11 {
execsql {
SELECT b FROM collate2t1 WHERE b >= 'aa' ORDER BY 1, oid;
}
} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
do_test collate2-1.12 {
execsql {
SELECT c FROM collate2t1 WHERE c >= 'aa' ORDER BY 1;
}
} {aa ba Ab Bb ab bb}
do_test collate2-1.13 {
execsql {
SELECT a FROM collate2t1 WHERE a <= 'aa' ORDER BY 1;
}
} {AA AB Aa Ab BA BB Ba Bb aA aB aa}
do_test collate2-1.14 {
execsql {
SELECT b FROM collate2t1 WHERE b <= 'aa' ORDER BY 1, oid;
}
} {aa aA Aa AA}
do_test collate2-1.15 {
execsql {
SELECT c FROM collate2t1 WHERE c <= 'aa' ORDER BY 1;
}
} {AA BA aA bA AB BB aB bB Aa Ba aa}
do_test collate2-1.16 {
execsql {
SELECT a FROM collate2t1 WHERE a BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
}
} {Aa Ab BA BB Ba Bb}
do_test collate2-1.17 {
execsql {
SELECT b FROM collate2t1 WHERE b BETWEEN 'Aa' AND 'Bb' ORDER BY 1, oid;
}
} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
do_test collate2-1.18 {
execsql {
SELECT c FROM collate2t1 WHERE c BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
}
} {Aa Ba aa ba Ab Bb}
do_test collate2-1.19 {
execsql {
SELECT a FROM collate2t1 WHERE
CASE a WHEN 'aa' THEN 1 ELSE 0 END
ORDER BY 1, oid;
}
} {aa}
do_test collate2-1.20 {
execsql {
SELECT b FROM collate2t1 WHERE
CASE b WHEN 'aa' THEN 1 ELSE 0 END
ORDER BY 1, oid;
}
} {aa aA Aa AA}
do_test collate2-1.21 {
execsql {
SELECT c FROM collate2t1 WHERE
CASE c WHEN 'aa' THEN 1 ELSE 0 END
ORDER BY 1, oid;
}
} {aa}
do_test collate2-1.22 {
execsql {
SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb') ORDER BY 1, oid;
}
} {aa bb}
do_test collate2-1.23 {
execsql {
SELECT b FROM collate2t1 WHERE b IN ('aa', 'bb') ORDER BY 1, oid;
}
} {aa aA Aa AA bb bB Bb BB}
do_test collate2-1.24 {
execsql {
SELECT c FROM collate2t1 WHERE c IN ('aa', 'bb') ORDER BY 1, oid;
}
} {aa bb}
do_test collate2-1.25 {
execsql {
SELECT a FROM collate2t1
WHERE a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {aa bb}
do_test collate2-1.26 {
execsql {
SELECT b FROM collate2t1
WHERE b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {aa bb aA bB Aa Bb AA BB}
do_test collate2-1.27 {
execsql {
SELECT c FROM collate2t1
WHERE c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {aa bb}
do_test collate2-2.1 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a > 'aa' ORDER BY 1;
}
} {AA AB Aa Ab BA BB Ba Bb aA aB aa}
do_test collate2-2.2 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b > 'aa' ORDER BY 1, oid;
}
} {aa aA Aa AA}
do_test collate2-2.3 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c > 'aa' ORDER BY 1;
}
} {AA BA aA bA AB BB aB bB Aa Ba aa}
do_test collate2-2.4 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a < 'aa' ORDER BY 1;
}
} {aa ab bA bB ba bb}
do_test collate2-2.5 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b < 'aa' ORDER BY 1, oid;
}
} {aa aA Aa AA ab aB Ab AB ba bA Ba BA bb bB Bb BB}
do_test collate2-2.6 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c < 'aa' ORDER BY 1;
}
} {aa ba Ab Bb ab bb}
do_test collate2-2.7 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a = 'aa';
}
} {ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.8 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b = 'aa';
}
} {ab ba bb aB bA bB Ab Ba Bb AB BA BB}
do_test collate2-2.9 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c = 'aa';
}
} {ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.10 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a >= 'aa' ORDER BY 1;
}
} {AA AB Aa Ab BA BB Ba Bb aA aB}
do_test collate2-2.11 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b >= 'aa' ORDER BY 1, oid;
}
} {}
do_test collate2-2.12 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c >= 'aa' ORDER BY 1;
}
} {AA BA aA bA AB BB aB bB Aa Ba}
do_test collate2-2.13 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a <= 'aa' ORDER BY 1;
}
} {ab bA bB ba bb}
do_test collate2-2.14 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b <= 'aa' ORDER BY 1, oid;
}
} {ab aB Ab AB ba bA Ba BA bb bB Bb BB}
do_test collate2-2.15 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c <= 'aa' ORDER BY 1;
}
} {ba Ab Bb ab bb}
do_test collate2-2.16 {
execsql {
SELECT a FROM collate2t1 WHERE a NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
}
} {AA AB aA aB aa ab bA bB ba bb}
do_test collate2-2.17 {
execsql {
SELECT b FROM collate2t1 WHERE b NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1, oid;
}
} {}
do_test collate2-2.18 {
execsql {
SELECT c FROM collate2t1 WHERE c NOT BETWEEN 'Aa' AND 'Bb' ORDER BY 1;
}
} {AA BA aA bA AB BB aB bB ab bb}
do_test collate2-2.19 {
execsql {
SELECT a FROM collate2t1 WHERE NOT CASE a WHEN 'aa' THEN 1 ELSE 0 END;
}
} {{} ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.20 {
execsql {
SELECT b FROM collate2t1 WHERE NOT CASE b WHEN 'aa' THEN 1 ELSE 0 END;
}
} {{} ab ba bb aB bA bB Ab Ba Bb AB BA BB}
do_test collate2-2.21 {
execsql {
SELECT c FROM collate2t1 WHERE NOT CASE c WHEN 'aa' THEN 1 ELSE 0 END;
}
} {{} ab ba bb aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.22 {
execsql {
SELECT a FROM collate2t1 WHERE NOT a IN ('aa', 'bb');
}
} {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.23 {
execsql {
SELECT b FROM collate2t1 WHERE NOT b IN ('aa', 'bb');
}
} {ab ba aB bA Ab Ba AB BA}
do_test collate2-2.24 {
execsql {
SELECT c FROM collate2t1 WHERE NOT c IN ('aa', 'bb');
}
} {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.25 {
execsql {
SELECT a FROM collate2t1
WHERE NOT a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-2.26 {
execsql {
SELECT b FROM collate2t1
WHERE NOT b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {ab ba aB bA Ab Ba AB BA}
do_test collate2-2.27 {
execsql {
SELECT c FROM collate2t1
WHERE NOT c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'));
}
} {ab ba aA aB bA bB Aa Ab Ba Bb AA AB BA BB}
do_test collate2-3.1 {
execsql {
SELECT a > 'aa' FROM collate2t1;
}
} {{} 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0}
do_test collate2-3.2 {
execsql {
SELECT b > 'aa' FROM collate2t1;
}
} {{} 0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1}
do_test collate2-3.3 {
execsql {
SELECT c > 'aa' FROM collate2t1;
}
} {{} 0 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0}
do_test collate2-3.4 {
execsql {
SELECT a < 'aa' FROM collate2t1;
}
} {{} 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1}
do_test collate2-3.5 {
execsql {
SELECT b < 'aa' FROM collate2t1;
}
} {{} 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.6 {
execsql {
SELECT c < 'aa' FROM collate2t1;
}
} {{} 0 0 0 0 1 1 1 1 1 0 1 0 1 1 1 1}
do_test collate2-3.7 {
execsql {
SELECT a = 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.8 {
execsql {
SELECT b = 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
do_test collate2-3.9 {
execsql {
SELECT c = 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.10 {
execsql {
SELECT a <= 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1}
do_test collate2-3.11 {
execsql {
SELECT b <= 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
do_test collate2-3.12 {
execsql {
SELECT c <= 'aa' FROM collate2t1;
}
} {{} 1 0 0 0 1 1 1 1 1 0 1 0 1 1 1 1}
do_test collate2-3.13 {
execsql {
SELECT a >= 'aa' FROM collate2t1;
}
} {{} 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0}
do_test collate2-3.14 {
execsql {
SELECT b >= 'aa' FROM collate2t1;
}
} {{} 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
do_test collate2-3.15 {
execsql {
SELECT c >= 'aa' FROM collate2t1;
}
} {{} 1 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0}
do_test collate2-3.16 {
execsql {
SELECT a BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
}
} {{} 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1}
do_test collate2-3.17 {
execsql {
SELECT b BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
}
} {{} 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
do_test collate2-3.18 {
execsql {
SELECT c BETWEEN 'Aa' AND 'Bb' FROM collate2t1;
}
} {{} 1 0 1 0 0 0 0 0 1 1 1 1 0 0 0 0}
do_test collate2-3.19 {
execsql {
SELECT CASE a WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
}
} {0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.20 {
execsql {
SELECT CASE b WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
}
} {0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0}
do_test collate2-3.21 {
execsql {
SELECT CASE c WHEN 'aa' THEN 1 ELSE 0 END FROM collate2t1;
}
} {0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.22 {
execsql {
SELECT a IN ('aa', 'bb') FROM collate2t1;
}
} {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.23 {
execsql {
SELECT b IN ('aa', 'bb') FROM collate2t1;
}
} {{} 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1}
do_test collate2-3.24 {
execsql {
SELECT c IN ('aa', 'bb') FROM collate2t1;
}
} {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.25 {
execsql {
SELECT a IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
FROM collate2t1;
}
} {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-3.26 {
execsql {
SELECT b IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
FROM collate2t1;
}
} {{} 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1}
do_test collate2-3.27 {
execsql {
SELECT c IN (SELECT a FROM collate2t1 WHERE a IN ('aa', 'bb'))
FROM collate2t1;
}
} {{} 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0}
do_test collate2-4.0 {
execsql {
CREATE TABLE collate2t2(b COLLATE binary);
CREATE TABLE collate2t3(b text);
INSERT INTO collate2t2 VALUES('aa');
INSERT INTO collate2t3 VALUES('aa');
}
} {}
# Test that when both sides of a binary comparison operator have
# default collation types, the collate type for the leftmost term
# is used.
do_test collate2-4.1 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t2
WHERE collate2t1.b = collate2t2.b;
}
} {aa aA Aa AA}
do_test collate2-4.2 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t2
WHERE collate2t2.b = collate2t1.b;
}
} {aa}
# Test that when one side has a default collation type and the other
# does not, the collation type is used.
do_test collate2-4.3 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t3
WHERE collate2t1.b = collate2t3.b||'';
}
} {aa aA Aa AA}
do_test collate2-4.4 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t3
WHERE collate2t3.b||'' = collate2t1.b;
}
} {aa aA Aa AA}
do_test collate2-4.5 {
execsql {
DROP TABLE collate2t3;
}
} {}
#
# Test that the default collation types are used when the JOIN syntax
# is used in place of a WHERE clause.
#
# SQLite transforms the JOIN syntax into a WHERE clause internally, so
# the focus of these tests is to ensure that the table on the left-hand-side
# of the join determines the collation type used.
#
do_test collate2-5.0 {
execsql {
SELECT collate2t1.b FROM collate2t1 JOIN collate2t2 USING (b);
}
} {aa aA Aa AA}
do_test collate2-5.1 {
execsql {
SELECT collate2t1.b FROM collate2t2 JOIN collate2t1 USING (b);
}
} {aa}
do_test collate2-5.2 {
execsql {
SELECT collate2t1.b FROM collate2t1 NATURAL JOIN collate2t2;
}
} {aa aA Aa AA}
do_test collate2-5.3 {
execsql {
SELECT collate2t1.b FROM collate2t2 NATURAL JOIN collate2t1;
}
} {aa}
do_test collate2-5.4 {
execsql {
SELECT collate2t2.b FROM collate2t1 LEFT OUTER JOIN collate2t2 USING (b) order by collate2t1.oid;
}
} {{} aa {} {} {} aa {} {} {} aa {} {} {} aa {} {} {}}
do_test collate2-5.5 {
execsql {
SELECT collate2t1.b, collate2t2.b FROM collate2t2 LEFT OUTER JOIN collate2t1 USING (b);
}
} {aa aa}
finish_test

860
test/collate4.test Normal file
View File

@ -0,0 +1,860 @@
#
# The author or author's hereby grant to the public domain a non-exclusive,
# fully paid-up, perpetual, license in the software and all related
# intellectual property to make, have made, use, have used, reproduce,
# prepare derivative works, distribute, perform and display the work.
#
#*************************************************************************
# This file implements regression tests for SQLite library. The
# focus of this file is testing indices that use user-defined collation
# sequences.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
db collate TEXT text_collate
proc text_collate {a b} {
return [string compare $a $b]
}
# Do an SQL statement. Append the search count to the end of the result.
#
proc count sql {
set ::sqlite_search_count 0
return [concat [execsql $sql] $::sqlite_search_count]
}
# This procedure executes the SQL. Then it checks the generated program
# for the SQL and appends a "nosort" to the result if the program contains the
# SortCallback opcode. If the program does not contain the SortCallback
# opcode it appends "sort"
#
proc cksort {sql} {
set data [execsql $sql]
set prog [execsql "EXPLAIN $sql"]
if {[regexp Sort $prog]} {set x sort} {set x nosort}
lappend data $x
return $data
}
#
# Test cases are organized roughly as follows:
#
# collate4-1.* ORDER BY.
# collate4-2.* WHERE clauses.
# collate4-3.* constraints (primary key, unique).
# collate4-4.* simple min() or max() queries.
# collate4-5.* REINDEX command
# collate4-6.* INTEGER PRIMARY KEY indices.
#
#
# These tests - collate4-1.* - check that indices are correctly
# selected or not selected to implement ORDER BY clauses when
# user defined collation sequences are involved.
#
# Because these tests also exercise all the different ways indices
# can be created, they also serve to verify that indices are correctly
# initialised with user-defined collation sequences when they are
# created.
#
# Tests named collate4-1.1.* use indices with a single column. Tests
# collate4-1.2.* use indices with two columns.
#
do_test collate4-1.1.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE NOCASE, b COLLATE TEXT);
INSERT INTO collate4t1 VALUES( 'a', 'a' );
INSERT INTO collate4t1 VALUES( 'b', 'b' );
INSERT INTO collate4t1 VALUES( NULL, NULL );
INSERT INTO collate4t1 VALUES( 'B', 'B' );
INSERT INTO collate4t1 VALUES( 'A', 'A' );
CREATE INDEX collate4i1 ON collate4t1(a);
CREATE INDEX collate4i2 ON collate4t1(b);
}
} {}
do_test collate4-1.1.1 {
cksort {SELECT a FROM collate4t1 ORDER BY a}
} {{} a A b B nosort}
do_test collate4-1.1.2 {
cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE NOCASE}
} {{} a A b B nosort}
do_test collate4-1.1.3 {
cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE TEXT}
} {{} A B a b sort}
do_test collate4-1.1.4 {
cksort {SELECT b FROM collate4t1 ORDER BY b}
} {{} A B a b nosort}
do_test collate4-1.1.5 {
cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE TEXT}
} {{} A B a b nosort}
do_test collate4-1.1.6 {
cksort {SELECT b FROM collate4t1 ORDER BY b COLLATE NOCASE}
} {{} A a B b sort}
do_test collate4-1.1.7 {
execsql {
CREATE TABLE collate4t2(
a PRIMARY KEY COLLATE NOCASE,
b UNIQUE COLLATE TEXT
);
INSERT INTO collate4t2 VALUES( 'a', 'a' );
INSERT INTO collate4t2 VALUES( NULL, NULL );
INSERT INTO collate4t2 VALUES( 'B', 'B' );
}
} {}
do_test collate4-1.1.8 {
cksort {SELECT a FROM collate4t2 ORDER BY a}
} {{} a B nosort}
do_test collate4-1.1.9 {
cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE NOCASE}
} {{} a B nosort}
do_test collate4-1.1.10 {
cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE TEXT}
} {{} B a sort}
do_test collate4-1.1.11 {
cksort {SELECT b FROM collate4t2 ORDER BY b}
} {{} B a nosort}
do_test collate4-1.1.12 {
cksort {SELECT b FROM collate4t2 ORDER BY b COLLATE TEXT}
} {{} B a nosort}
do_test collate4-1.1.13 {
cksort {SELECT b FROM collate4t2 ORDER BY b COLLATE NOCASE}
} {{} a B sort}
do_test collate4-1.1.14 {
execsql {
CREATE TABLE collate4t3(
b COLLATE TEXT,
a COLLATE NOCASE,
UNIQUE(a), PRIMARY KEY(b)
);
INSERT INTO collate4t3 VALUES( 'a', 'a' );
INSERT INTO collate4t3 VALUES( NULL, NULL );
INSERT INTO collate4t3 VALUES( 'B', 'B' );
}
} {}
do_test collate4-1.1.15 {
cksort {SELECT a FROM collate4t3 ORDER BY a}
} {{} a B nosort}
do_test collate4-1.1.16 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE NOCASE}
} {{} a B nosort}
do_test collate4-1.1.17 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE TEXT}
} {{} B a sort}
do_test collate4-1.1.18 {
cksort {SELECT b FROM collate4t3 ORDER BY b}
} {{} B a nosort}
do_test collate4-1.1.19 {
cksort {SELECT b FROM collate4t3 ORDER BY b COLLATE TEXT}
} {{} B a nosort}
do_test collate4-1.1.20 {
cksort {SELECT b FROM collate4t3 ORDER BY b COLLATE NOCASE}
} {{} a B sort}
do_test collate4-1.1.21 {
execsql {
CREATE TABLE collate4t4(a COLLATE NOCASE, b COLLATE TEXT);
INSERT INTO collate4t4 VALUES( 'a', 'a' );
INSERT INTO collate4t4 VALUES( 'b', 'b' );
INSERT INTO collate4t4 VALUES( NULL, NULL );
INSERT INTO collate4t4 VALUES( 'B', 'B' );
INSERT INTO collate4t4 VALUES( 'A', 'A' );
CREATE INDEX collate4i3 ON collate4t4(a COLLATE TEXT);
CREATE INDEX collate4i4 ON collate4t4(b COLLATE NOCASE);
}
} {}
do_test collate4-1.1.22 {
cksort {SELECT a FROM collate4t4 ORDER BY a}
} {{} A a B b sort}
do_test collate4-1.1.23 {
cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE NOCASE}
} {{} A a B b sort}
do_test collate4-1.1.24 {
cksort {SELECT a FROM collate4t4 ORDER BY a COLLATE TEXT}
} {{} A B a b nosort}
do_test collate4-1.1.25 {
cksort {SELECT b FROM collate4t4 ORDER BY b}
} {{} A B a b sort}
do_test collate4-1.1.26 {
cksort {SELECT b FROM collate4t4 ORDER BY b COLLATE TEXT}
} {{} A B a b sort}
do_test collate4-1.1.27 {
cksort {SELECT b FROM collate4t4 ORDER BY b COLLATE NOCASE}
} {{} a A b B nosort}
do_test collate4-1.1.30 {
execsql {
DROP TABLE collate4t1;
DROP TABLE collate4t2;
DROP TABLE collate4t3;
DROP TABLE collate4t4;
}
} {}
do_test collate4-1.2.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE NOCASE, b COLLATE TEXT);
INSERT INTO collate4t1 VALUES( 'a', 'a' );
INSERT INTO collate4t1 VALUES( 'b', 'b' );
INSERT INTO collate4t1 VALUES( NULL, NULL );
INSERT INTO collate4t1 VALUES( 'B', 'B' );
INSERT INTO collate4t1 VALUES( 'A', 'A' );
CREATE INDEX collate4i1 ON collate4t1(a, b);
}
} {}
do_test collate4-1.2.1 {
cksort {SELECT a FROM collate4t1 ORDER BY a}
} {{} A a B b nosort}
do_test collate4-1.2.2 {
cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE nocase}
} {{} A a B b nosort}
do_test collate4-1.2.3 {
cksort {SELECT a FROM collate4t1 ORDER BY a COLLATE text}
} {{} A B a b sort}
do_test collate4-1.2.4 {
cksort {SELECT a FROM collate4t1 ORDER BY a, b}
} {{} A a B b nosort}
do_test collate4-1.2.5 {
cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE nocase}
} {{} A a B b sort}
do_test collate4-1.2.6 {
cksort {SELECT a FROM collate4t1 ORDER BY a, b COLLATE text}
} {{} A a B b nosort}
do_test collate4-1.2.7 {
execsql {
CREATE TABLE collate4t2(
a COLLATE NOCASE,
b COLLATE TEXT,
PRIMARY KEY(a, b)
);
INSERT INTO collate4t2 VALUES( 'a', 'a' );
INSERT INTO collate4t2 VALUES( NULL, NULL );
INSERT INTO collate4t2 VALUES( 'B', 'B' );
}
} {}
do_test collate4-1.2.8 {
cksort {SELECT a FROM collate4t2 ORDER BY a}
} {{} a B nosort}
do_test collate4-1.2.9 {
cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE nocase}
} {{} a B nosort}
do_test collate4-1.2.10 {
cksort {SELECT a FROM collate4t2 ORDER BY a COLLATE text}
} {{} B a sort}
do_test collate4-1.2.11 {
cksort {SELECT a FROM collate4t2 ORDER BY a, b}
} {{} a B nosort}
do_test collate4-1.2.12 {
cksort {SELECT a FROM collate4t2 ORDER BY a, b COLLATE nocase}
} {{} a B sort}
do_test collate4-1.2.13 {
cksort {SELECT a FROM collate4t2 ORDER BY a, b COLLATE text}
} {{} a B nosort}
do_test collate4-1.2.14 {
execsql {
CREATE TABLE collate4t3(a COLLATE NOCASE, b COLLATE TEXT);
INSERT INTO collate4t3 VALUES( 'a', 'a' );
INSERT INTO collate4t3 VALUES( 'b', 'b' );
INSERT INTO collate4t3 VALUES( NULL, NULL );
INSERT INTO collate4t3 VALUES( 'B', 'B' );
INSERT INTO collate4t3 VALUES( 'A', 'A' );
CREATE INDEX collate4i2 ON collate4t3(a COLLATE TEXT, b COLLATE NOCASE);
}
} {}
do_test collate4-1.2.15 {
cksort {SELECT a FROM collate4t3 ORDER BY a}
} {{} A a B b sort}
do_test collate4-1.2.16 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE nocase}
} {{} A a B b sort}
do_test collate4-1.2.17 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text}
} {{} A B a b nosort}
do_test collate4-1.2.18 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b}
} {{} A B a b sort}
do_test collate4-1.2.19 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b COLLATE nocase}
} {{} A B a b nosort}
do_test collate4-1.2.20 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text, b COLLATE text}
} {{} A B a b sort}
do_test collate4-1.2.21 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text DESC}
} {b a B A {} nosort}
do_test collate4-1.2.22 {
cksort {SELECT a FROM collate4t3 ORDER BY a COLLATE text DESC, b}
} {b a B A {} sort}
do_test collate4-1.2.23 {
cksort {SELECT a FROM collate4t3
ORDER BY a COLLATE text DESC, b COLLATE nocase}
} {b a B A {} sort}
do_test collate4-1.2.24 {
cksort {SELECT a FROM collate4t3
ORDER BY a COLLATE text DESC, b COLLATE nocase DESC}
} {b a B A {} nosort}
do_test collate4-1.2.25 {
execsql {
DROP TABLE collate4t1;
DROP TABLE collate4t2;
DROP TABLE collate4t3;
}
} {}
#
# These tests - collate4-2.* - check that indices are correctly
# selected or not selected to implement WHERE clauses when user
# defined collation sequences are involved.
#
# Indices may optimise WHERE clauses using <, >, <=, >=, = or IN
# operators.
#
do_test collate4-2.1.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE NOCASE);
CREATE TABLE collate4t2(b COLLATE TEXT);
INSERT INTO collate4t1 VALUES('a');
INSERT INTO collate4t1 VALUES('A');
INSERT INTO collate4t1 VALUES('b');
INSERT INTO collate4t1 VALUES('B');
INSERT INTO collate4t1 VALUES('c');
INSERT INTO collate4t1 VALUES('C');
INSERT INTO collate4t1 VALUES('d');
INSERT INTO collate4t1 VALUES('D');
INSERT INTO collate4t1 VALUES('e');
INSERT INTO collate4t1 VALUES('D');
INSERT INTO collate4t2 VALUES('A');
INSERT INTO collate4t2 VALUES('Z');
}
} {}
do_test collate4-2.1.1 {
count {
SELECT * FROM collate4t2, collate4t1 WHERE a = b;
}
} {A a A A 19}
do_test collate4-2.1.2 {
execsql {
CREATE INDEX collate4i1 ON collate4t1(a);
}
count {
SELECT * FROM collate4t2, collate4t1 WHERE a = b;
}
} {A a A A 7}
do_test collate4-2.1.3 {
count {
SELECT * FROM collate4t2, collate4t1 WHERE b = a;
}
} {A A 19}
do_test collate4-2.1.4 {
execsql {
DROP INDEX collate4i1;
CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT);
}
count {
SELECT * FROM collate4t2, collate4t1 WHERE a = b;
}
} {A a A A 19}
do_test collate4-2.1.5 {
count {
SELECT * FROM collate4t2, collate4t1 WHERE b = a;
}
} {A A 5}
do_test collate4-2.1.6 {
count {
SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
}
} {a A 10}
do_test collate4-2.1.7 {
execsql {
DROP INDEX collate4i1;
CREATE INDEX collate4i1 ON collate4t1(a);
}
count {
SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
}
} {a A 8}
do_test collate4-2.1.8 {
count {
SELECT a FROM collate4t1 WHERE a IN ('z', 'a');
}
} {a A 7}
do_test collate4-2.1.9 {
execsql {
DROP INDEX collate4i1;
CREATE INDEX collate4i1 ON collate4t1(a COLLATE TEXT);
}
count {
SELECT a FROM collate4t1 WHERE a IN ('z', 'a');
}
} {a A 9}
do_test collate4-2.1.10 {
execsql {
DROP TABLE collate4t1;
DROP TABLE collate4t2;
}
} {}
do_test collate4-2.2.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE nocase, b COLLATE text, c);
CREATE TABLE collate4t2(a COLLATE nocase, b COLLATE text, c COLLATE TEXT);
INSERT INTO collate4t1 VALUES('0', '0', '0');
INSERT INTO collate4t1 VALUES('0', '0', '1');
INSERT INTO collate4t1 VALUES('0', '1', '0');
INSERT INTO collate4t1 VALUES('0', '1', '1');
INSERT INTO collate4t1 VALUES('1', '0', '0');
INSERT INTO collate4t1 VALUES('1', '0', '1');
INSERT INTO collate4t1 VALUES('1', '1', '0');
INSERT INTO collate4t1 VALUES('1', '1', '1');
insert into collate4t2 SELECT * FROM collate4t1;
}
} {}
do_test collate4-2.2.1 {
count {
SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
}
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 63}
do_test collate4-2.2.1 {
execsql {
CREATE INDEX collate4i1 ON collate4t1(a, b, c);
}
count {
SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
}
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 45}
do_test collate4-2.2.2 {
execsql {
DROP INDEX collate4i1;
CREATE INDEX collate4i1 ON collate4t1(a, b, c COLLATE text);
}
count {
SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
}
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 22}
do_test collate4-2.2.10 {
execsql {
DROP TABLE collate4t1;
DROP TABLE collate4t2;
}
} {}
#
# These tests - collate4-3.* verify that indices that implement
# UNIQUE and PRIMARY KEY constraints operate correctly with user
# defined collation sequences.
#
do_test collate4-3.0 {
execsql {
CREATE TABLE collate4t1(a PRIMARY KEY COLLATE NOCASE);
}
} {}
do_test collate4-3.1 {
catchsql {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
} {1 {column a is not unique}}
do_test collate4-3.2 {
execsql {
SELECT * FROM collate4t1;
}
} {abc}
do_test collate4-3.3 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
} {1 {column a is not unique}}
do_test collate4-3.4 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
} {1 {column a is not unique}}
do_test collate4-3.5 {
execsql {
DROP TABLE collate4t1;
CREATE TABLE collate4t1(a COLLATE NOCASE UNIQUE);
}
} {}
do_test collate4-3.6 {
catchsql {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
} {1 {column a is not unique}}
do_test collate4-3.7 {
execsql {
SELECT * FROM collate4t1;
}
} {abc}
do_test collate4-3.8 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
} {1 {column a is not unique}}
do_test collate4-3.9 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
} {1 {column a is not unique}}
do_test collate4-3.10 {
execsql {
DROP TABLE collate4t1;
CREATE TABLE collate4t1(a);
CREATE UNIQUE INDEX collate4i1 ON collate4t1(a COLLATE NOCASE);
}
} {}
do_test collate4-3.11 {
catchsql {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
} {1 {column a is not unique}}
do_test collate4-3.12 {
execsql {
SELECT * FROM collate4t1;
}
} {abc}
do_test collate4-3.13 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
} {1 {column a is not unique}}
do_test collate4-3.14 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
} {1 {column a is not unique}}
do_test collate4-3.15 {
execsql {
DROP TABLE collate4t1;
}
} {}
#
# These tests - collate4-4.* check that min() and max() only ever
# use indices constructed with built-in collation type numeric.
#
# CHANGED: min() and max() now use the collation type. If there
# is an indice that can be used, it is used.
#
# FIX ME: min() and max() are currently broken.
if 0 {
do_test collate4-4.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE TEXT);
INSERT INTO collate4t1 VALUES(2);
INSERT INTO collate4t1 VALUES(10);
INSERT INTO collate4t1 VALUES(20);
INSERT INTO collate4t1 VALUES(104);
}
} {}
do_test collate4-4.1 {
count {
SELECT max(a) FROM collate4t1
}
} {20 3}
do_test collate4-4.2 {
count {
SELECT min(a) FROM collate4t1
}
} {10 3}
do_test collate4-4.3 {
# Test that the index with collation type TEXT is used.
execsql {
CREATE INDEX collate4i1 ON collate4t1(a);
}
count {
SELECT min(a) FROM collate4t1;
}
} {10 1}
do_test collate4-4.4 {
count {
SELECT max(a) FROM collate4t1;
}
} {20 1}
do_test collate4-4.5 {
# Test that the index with collation type NUMERIC is not used.
execsql {
DROP INDEX collate4i1;
CREATE INDEX collate4i1 ON collate4t1(a COLLATE NUMERIC);
}
count {
SELECT min(a) FROM collate4t1;
}
} {10 3}
do_test collate4-4.6 {
count {
SELECT max(a) FROM collate4t1;
}
} {20 3}
do_test collate4-4.7 {
execsql {
DROP TABLE collate4t1;
}
} {}
# Also test the scalar min() and max() functions.
#
do_test collate4-4.8 {
execsql {
CREATE TABLE collate4t1(a NUMERIC, b TEXT,
c COLLATE TEXT, d COLLATE NUMERIC);
INSERT INTO collate4t1 VALUES(11, 101, 1001, 10001);
INSERT INTO collate4t1 VALUES(20002, 2002, 202, 22);
}
} {}
do_test collate4-4.9 {
execsql {
SELECT max(a, b, c) FROM collate4t1;
}
} {11 202}
do_test collate4-4.10 {
execsql {
SELECT max(c, b, a) FROM collate4t1;
}
} {11 202}
do_test collate4-4.11 {
execsql {
SELECT max(a, b) FROM collate4t1;
}
} {101 20002}
do_test collate4-4.12 {
execsql {
SELECT max(b, a) FROM collate4t1;
}
} {101 20002}
do_test collate4-4.13 {
execsql {
SELECT max(b, a) FROM collate4t1;
}
} {101 20002}
do_test collate4-4.14 {
execsql {
SELECT max(b, '11') FROM collate4t1;
}
} {11 2002}
do_test collate4-4.15 {
execsql {
SELECT max('11', b) FROM collate4t1;
}
} {11 2002}
do_test collate4-4.16 {
execsql {
SELECT max(11, b) FROM collate4t1;
}
} {101 2002}
do_test collate4-4.17 {
execsql {
SELECT max(b, 11) FROM collate4t1;
}
} {101 2002}
do_test collate4-4.18 {
execsql {
SELECT max(c, d) FROM collate4t1;
}
} {1001 22}
do_test collate4-4.19 {
execsql {
SELECT max(d, c) FROM collate4t1;
}
} {10001 202}
do_test collate4-4.20 {
execsql {
DROP TABLE collate4t1;
}
} {}
}
#
# These tests - collate4-5.* - test the REINDEX command.
#
# FIX ME: Find out if version 3 needs REINDEX.
if 0 {
proc install_normal_collate {} {
db collate collate1 "string compare"
}
proc inverse_collate {l r} {
expr -1 * [string compare $l $r]
}
proc install_inverse_collate {} {
db collate collate1 inverse_collate
}
install_normal_collate
do_test collate4-5.0 {
execsql {
CREATE TABLE collate4t1(a COLLATE collate1);
INSERT INTO collate4t1 VALUES('A');
INSERT INTO collate4t1 VALUES(NULL);
INSERT INTO collate4t1 VALUES('B');
CREATE INDEX collate4i1 ON collate4t1(a);
}
} {}
do_test collate4-5.1 {
cksort {
SELECT * FROM collate4t1 ORDER BY 1;
}
} {{} A B nosort}
do_test collate4-5.2 {
install_inverse_collate
cksort {
SELECT * FROM collate4t1 ORDER BY 1;
}
} {{} A B nosort} ;# This is incorrect - because we need to REINDEX
do_test collate4-5.3 {
install_inverse_collate
cksort {
REINDEX collate4t1;
SELECT * FROM collate4t1 ORDER BY 1;
}
} {{} B A nosort}
do_test collate4-5.4 {
install_normal_collate
cksort {
REINDEX;
SELECT * FROM collate4t1 ORDER BY 1;
}
} {{} A B nosort}
do_test collate4-5.5 {
install_inverse_collate
cksort {
REINDEX main.collate4t1;
SELECT * FROM collate4t1 ORDER BY 1;
}
} {{} B A nosort}
do_test collate4-5.6 {
catchsql {
REINDEX garbage;
}
} {1 {no such table: garbage}}
do_test collate4-5.7 {
execsql {
DROP TABLE collate4t1;
CREATE TEMP TABLE collate4t1(a COLLATE collate1, b COLLATE collate1);
CREATE INDEX collatei1 ON collate4t1(a);
CREATE INDEX collatei2 ON collate4t1(b);
INSERT INTO collate4t1 VALUES(1, 1);
INSERT INTO collate4t1 VALUES(NULL, NULL);
INSERT INTO collate4t1 VALUES(2, 2);
}
} {}
do_test collate4-5.8 {
cksort {
SELECT * FROM collate4t1 ORDER BY 1
}
} {{} {} 2 2 1 1 nosort}
do_test collate4-5.9 {
install_normal_collate
cksort {
REINDEX;
SELECT * FROM collate4t1 order by 2;
}
} {{} {} 1 1 2 2 nosort}
do_test collate4-5.10 {
install_inverse_collate
cksort {
REINDEX collate4t1;
SELECT * FROM collate4t1 order by 1;
}
} {{} {} 2 2 1 1 nosort}
do_test collate4-5.11 {
install_normal_collate
cksort {
REINDEX temp.collate4t1;
SELECT * FROM collate4t1 order by 2;
}
} {{} {} 1 1 2 2 nosort}
# This checks that if a REINDEX operation produces a conflict an error
# is raised and the checkpoint rolled back.
do_test collate4-5.12 {
execsql {
BEGIN;
CREATE UNIQUE INDEX collate4i3 ON collate4t1(a);
INSERT INTO collate4t1 VALUES(3, 3);
}
db collate collate1 "expr 0 ;"
catchsql {
REINDEX;
}
} {1 {indexed columns are not unique}}
do_test collate4-5.13 {
execsql {
COMMIT;
SELECT * FROM collate4t1;
}
} {1 1 {} {} 2 2 3 3}
# Do an EXPLAIN REINDEX, just in case it leaks memory or something.
do_test collate4-5.14 {
execsql {
EXPLAIN REINDEX;
}
expr 0
} {0}
do_test collate4-5.15 {
execsql {
EXPLAIN REINDEX collate4t1;
}
expr 0
} {0}
do_test collate4-5.16 {
execsql {
DROP TABLE collate4t1;
}
} {}
}
#
# These tests - collate4.6.* - ensure that implict INTEGER PRIMARY KEY
# indices do not confuse collation sequences.
#
# These indices are never used for sorting in SQLite. And you can't
# create another index on an INTEGER PRIMARY KEY column, so we don't have
# to test that.
#
do_test collate4-6.0 {
execsql {
CREATE TABLE collate4t1(a INTEGER PRIMARY KEY);
INSERT INTO collate4t1 VALUES(101);
INSERT INTO collate4t1 VALUES(10);
INSERT INTO collate4t1 VALUES(15);
}
} {}
do_test collate4-6.1 {
cksort {
SELECT * FROM collate4t1 ORDER BY 1;
}
} {10 15 101 sort}
do_test collate4-6.2 {
cksort {
SELECT * FROM collate4t1 ORDER BY oid;
}
} {10 15 101 sort}
do_test collate4-6.3 {
cksort {
SELECT * FROM collate4t1 ORDER BY oid||'' COLLATE TEXT;
}
} {10 101 15 sort}
finish_test

View File

@ -1,4 +1,4 @@
# 2001 September 15
# 2001 September 15.
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
@ -13,11 +13,24 @@
# This file implements tests for miscellanous features that were
# left out of other test files.
#
# $Id: misc1.test,v 1.25 2004/05/31 08:26:49 danielk1977 Exp $
# $Id: misc1.test,v 1.26 2004/06/09 09:55:20 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Mimic the SQLite 2 collation type NUMERIC.
db collate numeric numeric_collate
proc numeric_collate {lhs rhs} {
if {$lhs == $rhs} {return 0}
return [expr ($lhs>$rhs)?1:-1]
}
# Mimic the SQLite 2 collation type TEXT.
db collate text text_collate
proc numeric_collate {lhs rhs} {
return [string compare $lhs $rhs]
}
# Test the creation and use of tables that have a large number
# of columns.
#
@ -381,11 +394,14 @@ do_test misc1-12.9 {
# identifiers can be used as a collating sequence. Collation is by text
# if the identifier contains "text", "blob", or "clob" and is numeric
# otherwise.
do_test misc1-12.10 {
catchsql {
SELECT * FROM t6 ORDER BY a COLLATE unknown;
}
} {0 {0 0.0 y 0}}
#
# Update: In v3, it is an error again.
#
#do_test misc1-12.10 {
# catchsql {
# SELECT * FROM t6 ORDER BY a COLLATE unknown;
# }
#} {0 {0 0.0 y 0}}
do_test misc1-12.11 {
execsql {
CREATE TABLE t8(x TEXT COLLATE numeric, y INTEGER COLLATE text, z);

View File

@ -1,4 +1,4 @@
# 2001 September 15
# 2001 September 15.
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE TABLE statement.
#
# $Id: sort.test,v 1.12 2004/05/27 17:22:56 drh Exp $
# $Id: sort.test,v 1.13 2004/06/09 09:55:20 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -323,26 +323,26 @@ do_test sort-7.8 {
# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE integer;
# }
#} {1 2 11 12}
do_test sort-7.11 {
execsql {
SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE text;
}
} {1 11 12 2}
do_test sort-7.12 {
execsql {
SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE blob;
}
} {1 11 12 2}
do_test sort-7.13 {
execsql {
SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE clob;
}
} {1 11 12 2}
do_test sort-7.14 {
execsql {
SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE varchar;
}
} {1 11 12 2}
#do_test sort-7.11 {
# execsql {
# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE text;
# }
#} {1 11 12 2}
#do_test sort-7.12 {
# execsql {
# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE blob;
# }
#} {1 11 12 2}
#do_test sort-7.13 {
# execsql {
# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE clob;
# }
#} {1 11 12 2}
#do_test sort-7.14 {
# execsql {
# SELECT b FROM t4 UNION SELECT b FROM v4 ORDER BY 1 COLLATE varchar;
# }
#} {1 11 12 2}
# Ticket #297
#

View File

@ -15,7 +15,7 @@
# interface is pretty well tested. This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.21 2004/06/02 00:41:10 drh Exp $
# $Id: tclsqlite.test,v 1.22 2004/06/09 09:55:20 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -34,7 +34,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be authorizer, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, last_statement_changes, onecolumn, progress, rekey, timeout, or trace}}
} {1 {bad option "bogus": must be authorizer, busy, changes, close, commit_hook, complete, errorcode, eval, function, last_insert_rowid, last_statement_changes, onecolumn, progress, rekey, timeout, trace, or collate}}
do_test tcl-1.3 {
execsql {CREATE TABLE t1(a int, b int)}
execsql {INSERT INTO t1 VALUES(10,20)}

View File

@ -1,4 +1,4 @@
set rcsid {$Id: datatype3.tcl,v 1.3 2004/06/01 10:01:25 drh Exp $}
set rcsid {$Id: datatype3.tcl,v 1.4 2004/06/09 09:55:20 danielk1977 Exp $}
source common.tcl
header {Datatypes In SQLite Version 3}
puts {
@ -44,7 +44,8 @@ database engine may convert values between numeric storage classes
quotes, INTEGER if the literal is specified as an unquoted number
with no decimal point or exponent, REAL if the literal is an
unquoted number with a decimal point or exponent and NULL if the
value is a NULL.</P>
value is a NULL. Literals with storage class BLOB are specified
using the X'ABCD' notation.</P>
<LI><P>Values supplied using the sqlite3_bind_* APIs are assigned
the storage class that most closely matches the native type bound
(i.e. sqlite3_bind_blob() binds a value with storage class BLOB).</P>
@ -199,6 +200,22 @@ SQL scalar expression or literal other than a column value.</P>
place.</P>
</UL>
<P>
In SQLite, the expression "a BETWEEN b AND c" is equivalent to "a &gt;= b
AND a &lt;= c", even if this means that different affinities are applied to
'a' in each of the comparisons required to evaluate the expression.
</P>
<P>Expressions of the type "a IN (SELECT b ....)" are handled by the three
rules enumerated above for binary comparisons (e.g. in a
similar manner to "a = b"). For example if 'b' is a column value
and 'a' is an expression, then the affinity of 'b' is applied to 'a'
before any comparisons take place.</P>
<P>SQLite treats the expression "a IN (x, y, z)" as equivalent to "a = z OR
a = y OR a = z".
</P>
<h4>3.1 Comparison Example</h4>
<blockquote>
@ -227,25 +244,6 @@ SELECT c &lt; 60, c &lt; 600 FROM t1;
0|0
</PRE>
</blockquote>
<P>
In SQLite, the expression "a BETWEEN b AND c" is currently
equivalent to "a &gt;= b AND a &lt;= c". SQLite will continue to
treat the two as exactly equivalent, even if this means that
different affinities are applied to 'a' in each of the comparisons
required to evaluate the expression.</P>
<P>Expressions of the type "a IN (SELECT b ....)" are handled by
the three rules enumerated above for binary comparisons (e.g. in a
similar manner to "a = b"). For example if 'b' is a column value
and 'a' is an expression, then the affinity of 'b' is applied to 'a'
before any comparisons take place.</P>
<P>SQLite currently treats the expression "a IN (x, y, z)" as
equivalent to "a = z OR a = y OR a = z". SQLite will continue to
treat the two as exactly equivalent, even if this means that
different affinities are applied to 'a' in each of the comparisons
required to evaluate the expression.</P>
<h3>4. Operators</h3>
<P>All mathematical operators (which is to say, all operators other
@ -302,11 +300,121 @@ modes, as follows:</P>
<h3>7. User-defined Collation Sequences</h3>
<P>By default, when SQLite compares two
text values, the result of the comparison is determined using
memcmp(), regardless of the encoding of the string. SQLite v3
provides the ability for users to supply arbitrary comparison
functions, known as user-defined collation sequences, to be used
instead of memcmp().</P>
<p>
By default, when SQLite compares two text values, the result of the
comparison is determined using memcmp(), regardless of the encoding of the
string. SQLite v3 provides the ability for users to supply arbitrary
comparison functions, known as user-defined collation sequences, to be used
instead of memcmp().
</p>
<p>
Aside from the default collation sequence BINARY, implemented using
memcmp(), SQLite features two extra built-in collation sequences,
NOCASE and REVERSE:
</p>
<UL>
<LI><b>BINARY</b> - Compares string data using memcmp(), regardless
of text encoding.</LI>
<LI><b>REVERSE</b> - Collate in the reverse order to BINARY. </LI>
<LI><b>NOCASE</b> - The same as binary, except the 26 upper case
characters used by the English language are
folded to their lower case equivalents before
the comparison is performed. </UL>
<h4>7.1 Assigning Collation Sequences from SQL</h4>
<p>
Each column of each table has a default collation type. If a collation type
other than BINARY is required, a COLLATE clause is specified as part of the
<a href="lang.html#createtable">column definition</a> to define it.used as
illustrated in the example below to
</p>
<p>
Whenever two text values are compared by SQLite, a collation sequence is
used to determine the results of the comparison according to the following
rules. Sections 3 and 5 of this document describe the circumstances under
which such a comparison takes place.
</p>
<p>
For binary comparison operators (=, <, >, <= and >=) if either operand is a
column, then the default collation type of the column determines the
collation sequence to use for the comparison. If both operands are columns,
then the collation type for the left operand determines the collation
sequence used. If neither operand is a column, then the BINARY collation
sequence is used.
</p>
<p>
The expression "x BETWEEN y and z" is equivalent to "x &gt;= y AND x &lt;=
z". The expression "x IN (SELECT y ...)" is handled in the same way as the
expression "x = y" for the purposes of determining the collation sequence
to use. The collation sequence used for expressions of the form "x IN (y, z
...)" is the default collation type of x if x is a column, or BINARY
otherwise.
</p>
<p>
An <a href="lang.html#select">ORDER BY</a> clause that is part of a SELECT
statement may be assigned a collation sequence to be used for the sort
operation explicitly. In this case the explicit collation sequence is
always used. Otherwise, if the expression sorted by an ORDER BY clause is
a column, then the default collation type of the column is used to
determine sort order. If the expression is not a column, then the BINARY
collation sequence is used.
</p>
<h4>7.2 Collation Sequences Example</h4>
<p>
The examples below identify the collation sequences that would be used to
determine the results of text comparisons that may be performed by various
SQL statements. Note that a text comparison may not be required, and no
collation sequence used, in the case of numeric, blob or NULL values.
</p>
<blockquote>
<PRE>
CREATE TABLE t1(
a, -- default collation type BINARY
b COLLATE BINARY, -- default collation type BINARY
c COLLATE REVERSE, -- default collation type REVERSE
d COLLATE NOCASE -- default collation type NOCASE
);
-- Text comparison is performed using the BINARY collation sequence.
SELECT (a = b) FROM t1;
-- Text comparison is performed using the NOCASE collation sequence.
SELECT (a = d) FROM t1;
-- Text comparison is performed using the BINARY collation sequence.
SELECT (d = a) FROM t1;
-- Text comparison is performed using the REVERSE collation sequence.
SELECT ('abc' = c) FROM t1;
-- Text comparison is performed using the REVERSE collation sequence.
SELECT (c = 'abc') FROM t1;
-- Grouping is performed using the NOCASE collation sequence (i.e. values
-- 'abc' and 'ABC' are placed in the same group).
SELECT count(*) GROUP BY d FROM t1;
-- Grouping is performed using the BINARY collation sequence.
SELECT count(*) GROUP BY (d || '') FROM t1;
-- Sorting is performed using the REVERSE collation sequence.
SELECT * FROM t1 ORDER BY c;
-- Sorting is performed using the BINARY collation sequence.
SELECT * FROM t1 ORDER BY (c || '');
-- Sorting is performed using the NOCASE collation sequence.
SELECT * FROM t1 ORDER BY c COLLATE NOCASE;
</PRE>
</blockquote>
}
footer $rcsid