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:
53
manifest
53
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
1086196460e261718e78512d77e25dde021a117d
|
||||
c634e71f1909819fb55c728bc410e5cc390428e3
|
147
src/build.c
147
src/build.c
@ -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;
|
||||
|
126
src/expr.c
126
src/expr.c
@ -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: {
|
||||
|
76
src/main.c
76
src/main.c
@ -14,7 +14,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.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;
|
||||
}
|
||||
|
||||
|
33
src/parse.y
33
src/parse.y
@ -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 /////////////////////////
|
||||
//
|
||||
|
28
src/select.c
28
src/select.c
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
44
src/test1.c
44
src/test1.c
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.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 },
|
||||
|
54
src/test5.c
54
src/test5.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/*
|
||||
|
10
src/vdbe.c
10
src/vdbe.c
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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().
|
||||
*/
|
||||
|
45
src/where.c
45
src/where.c
@ -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
226
test/collate1.test
Normal 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
604
test/collate2.test
Normal 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
860
test/collate4.test
Normal 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
|
@ -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);
|
||||
|
@ -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
|
||||
#
|
||||
|
@ -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)}
|
||||
|
@ -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 >= b
|
||||
AND a <= 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 < 60, c < 600 FROM t1;
|
||||
0|0
|
||||
</PRE>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
In SQLite, the expression "a BETWEEN b AND c" is currently
|
||||
equivalent to "a >= b AND a <= 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 >= y AND x <=
|
||||
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
|
||||
|
Reference in New Issue
Block a user