SQLite4
Check-in [596c1f3869]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Allow collation sequence comparison functions to return errors.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 596c1f3869b5c75422166d65ff84191b59e9fd67
User & Date: dan 2013-06-07 19:29:01
Context
2013-06-10
19:52
Add the sqlite4_translate() API, for translating between utf-8 and utf-16 text encodings. Also the sqlite4_buffer object. check-in: f56bc22c9e user: dan tags: trunk
2013-06-07
19:29
Allow collation sequence comparison functions to return errors. check-in: 596c1f3869 user: dan tags: trunk
14:36
Change the order of the parameters passed to sqlite4_collation_needed() and needed16() to be consistent with other callback APIs. check-in: 22b6bdf65a user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/func.c.

45
46
47
48
49
50
51

52
53





54
55
56
57
58
59
60
...
684
685
686
687
688
689
690

691
692
693



694
695
696
697
698
699
700
....
1248
1249
1250
1251
1252
1253
1254

1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265




1266
1267
1268
1269
1270
1271
1272
  mask = sqlite4_context_appdata(context)==0 ? 0 : -1;
  pColl = sqlite4GetFuncCollSeq(context);
  assert( pColl );
  assert( mask==-1 || mask==0 );
  iBest = 0;
  if( sqlite4_value_type(argv[0])==SQLITE4_NULL ) return;
  for(i=1; i<argc; i++){

    if( sqlite4_value_type(argv[i])==SQLITE4_NULL ) return;
    if( (sqlite4MemCompare(argv[iBest], argv[i], pColl)^mask)>=0 ){





      testcase( mask==0 );
      iBest = i;
    }
  }
  sqlite4_result_value(context, argv[iBest]);
}

................................................................................
** arguments are equal to each other.
*/
static void nullifFunc(
  sqlite4_context *context,
  int NotUsed,
  sqlite4_value **argv
){

  CollSeq *pColl = sqlite4GetFuncCollSeq(context);
  UNUSED_PARAMETER(NotUsed);
  if( sqlite4MemCompare(argv[0], argv[1], pColl)!=0 ){



    sqlite4_result_value(context, argv[0]);
  }
}

/*
** Implementation of the sqlite_version() function.  The result is the version
** of the SQLite library that is running.
................................................................................
  if( sqlite4_value_type(argv[0])==SQLITE4_NULL ) return;
  pBest = (Mem *)sqlite4_aggregate_context(context, sizeof(*pBest));
  if( !pBest ) return;

  if( pBest->flags ){
    int max;
    int cmp;

    CollSeq *pColl = sqlite4GetFuncCollSeq(context);
    /* This step function is used for both the min() and max() aggregates,
    ** the only difference between the two being that the sense of the
    ** comparison is inverted. For the max() aggregate, the
    ** sqlite4_context_appdata() function returns (void *)-1. For min() it
    ** returns (void *)db, where db is the sqlite4* database pointer.
    ** Therefore the next statement sets variable 'max' to 1 for the max()
    ** aggregate, or 0 for min().
    */
    max = sqlite4_context_appdata(context)!=0;
    cmp = sqlite4MemCompare(pBest, pArg, pColl);




    if( (max && cmp<0) || (!max && cmp>0) ){
      sqlite4VdbeMemCopy(pBest, pArg);
    }
  }else{
    sqlite4VdbeMemCopy(pBest, pArg);
  }
}







>

|
>
>
>
>
>







 







>


|
>
>
>







 







>










|
>
>
>
>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
...
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
....
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
  mask = sqlite4_context_appdata(context)==0 ? 0 : -1;
  pColl = sqlite4GetFuncCollSeq(context);
  assert( pColl );
  assert( mask==-1 || mask==0 );
  iBest = 0;
  if( sqlite4_value_type(argv[0])==SQLITE4_NULL ) return;
  for(i=1; i<argc; i++){
    int res, rc;
    if( sqlite4_value_type(argv[i])==SQLITE4_NULL ) return;
    rc = sqlite4MemCompare(argv[iBest], argv[i], pColl, &res);
    if( rc!=SQLITE4_OK ){
      sqlite4_result_error_code(context, rc);
      return;
    }
    if( (res^mask)>=0 ){
      testcase( mask==0 );
      iBest = i;
    }
  }
  sqlite4_result_value(context, argv[iBest]);
}

................................................................................
** arguments are equal to each other.
*/
static void nullifFunc(
  sqlite4_context *context,
  int NotUsed,
  sqlite4_value **argv
){
  int rc, res;
  CollSeq *pColl = sqlite4GetFuncCollSeq(context);
  UNUSED_PARAMETER(NotUsed);
  rc = sqlite4MemCompare(argv[0], argv[1], pColl, &res);
  if( rc!=SQLITE4_OK ){
    sqlite4_result_error_code(context, rc);
  }else if( res ){
    sqlite4_result_value(context, argv[0]);
  }
}

/*
** Implementation of the sqlite_version() function.  The result is the version
** of the SQLite library that is running.
................................................................................
  if( sqlite4_value_type(argv[0])==SQLITE4_NULL ) return;
  pBest = (Mem *)sqlite4_aggregate_context(context, sizeof(*pBest));
  if( !pBest ) return;

  if( pBest->flags ){
    int max;
    int cmp;
    int rc;
    CollSeq *pColl = sqlite4GetFuncCollSeq(context);
    /* This step function is used for both the min() and max() aggregates,
    ** the only difference between the two being that the sense of the
    ** comparison is inverted. For the max() aggregate, the
    ** sqlite4_context_appdata() function returns (void *)-1. For min() it
    ** returns (void *)db, where db is the sqlite4* database pointer.
    ** Therefore the next statement sets variable 'max' to 1 for the max()
    ** aggregate, or 0 for min().
    */
    max = sqlite4_context_appdata(context)!=0;
    rc = sqlite4MemCompare(pBest, pArg, pColl, &cmp);
    if( rc!=SQLITE4_OK ){
      sqlite4_result_error_code(context, rc);
      return;
    }
    if( (max && cmp<0) || (!max && cmp>0) ){
      sqlite4VdbeMemCopy(pBest, pArg);
    }
  }else{
    sqlite4VdbeMemCopy(pBest, pArg);
  }
}

Changes to src/main.c.

212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
...
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274

275
276
277
278
279
280
281
282
...
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
....
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
**
** If the padFlag argument is not NULL then space padding at the end
** of strings is ignored.  This implements the RTRIM collation.
*/
static int binCollFunc(
  void *padFlag,
  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 ){
    if( padFlag
     && allSpaces(((char*)pKey1)+n, nKey1-n)
     && allSpaces(((char*)pKey2)+n, nKey2-n)
    ){
      /* Leave rc unchanged at 0 */
    }else{
      rc = nKey1 - nKey2;
    }
  }

  return rc;
}

/*
** The xMakeKey callback for the built-in RTRIM collation. The output
** is the same as the input, with any trailing ' ' characters removed.
** (e.g.  " abc   "  ->   " abc").
*/
................................................................................
** extends only to the 26 characters used in the English language.
**
** At the moment there is only a UTF-8 implementation.
*/
static int collNocaseCmp(
  void *NotUsed,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2

){
  int r = sqlite4_strnicmp(
      (const char *)pKey1, (const char *)pKey2, (nKey1<nKey2)?nKey1:nKey2);
  UNUSED_PARAMETER(NotUsed);
  if( 0==r ){
    r = nKey1-nKey2;
  }

  return r;
}

static int collNocaseMkKey(
  void *NotUsed,
  int nIn, const void *pKey1,
  int nOut, void *pKey2
){
................................................................................
** and the encoding is enc.
*/
static int createCollation(
  sqlite4* db,
  const char *zName, 
  u8 enc,
  void* pCtx,
  int(*xCompare)(void*,int,const void*,int,const void*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDel)(void*)
){
  CollSeq *pColl;
  int enc2;
  int nName = sqlite4Strlen30(zName);
  
................................................................................
** Register a new collation sequence with the database handle db.
*/
int sqlite4_create_collation(
  sqlite4* db, 
  const char *zName, 
  int enc, 
  void* pCtx,
  int(*xCompare)(void*,int,const void*,int,const void*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDel)(void*)
){
  int rc;
  sqlite4_mutex_enter(db->mutex);
  assert( !db->mallocFailed );
  rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xMakeKey, xDel);







|
>

|

|
|




|

|


>
|







 







|
>







>
|







 







|







 







|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
....
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
**
** If the padFlag argument is not NULL then space padding at the end
** of strings is ignored.  This implements the RTRIM collation.
*/
static int binCollFunc(
  void *padFlag,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2,
  int *pRes
){
  int res, n;
  n = nKey1<nKey2 ? nKey1 : nKey2;
  res = memcmp(pKey1, pKey2, n);
  if( res==0 ){
    if( padFlag
     && allSpaces(((char*)pKey1)+n, nKey1-n)
     && allSpaces(((char*)pKey2)+n, nKey2-n)
    ){
      /* Leave res unchanged at 0 */
    }else{
      res = nKey1 - nKey2;
    }
  }
  *pRes = res;
  return SQLITE4_OK;
}

/*
** The xMakeKey callback for the built-in RTRIM collation. The output
** is the same as the input, with any trailing ' ' characters removed.
** (e.g.  " abc   "  ->   " abc").
*/
................................................................................
** extends only to the 26 characters used in the English language.
**
** At the moment there is only a UTF-8 implementation.
*/
static int collNocaseCmp(
  void *NotUsed,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2,
  int *pRes
){
  int r = sqlite4_strnicmp(
      (const char *)pKey1, (const char *)pKey2, (nKey1<nKey2)?nKey1:nKey2);
  UNUSED_PARAMETER(NotUsed);
  if( 0==r ){
    r = nKey1-nKey2;
  }
  *pRes = r;
  return SQLITE4_OK;
}

static int collNocaseMkKey(
  void *NotUsed,
  int nIn, const void *pKey1,
  int nOut, void *pKey2
){
................................................................................
** and the encoding is enc.
*/
static int createCollation(
  sqlite4* db,
  const char *zName, 
  u8 enc,
  void* pCtx,
  int(*xCompare)(void*,int,const void*,int,const void*,int*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDel)(void*)
){
  CollSeq *pColl;
  int enc2;
  int nName = sqlite4Strlen30(zName);
  
................................................................................
** Register a new collation sequence with the database handle db.
*/
int sqlite4_create_collation(
  sqlite4* db, 
  const char *zName, 
  int enc, 
  void* pCtx,
  int(*xCompare)(void*,int,const void*,int,const void*,int*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDel)(void*)
){
  int rc;
  sqlite4_mutex_enter(db->mutex);
  assert( !db->mallocFailed );
  rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xMakeKey, xDel);

Changes to src/sqlite.h.in.

2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
....
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
** See also:  [sqlite4_collation_needed()] and [sqlite4_collation_needed16()].
*/
int sqlite4_create_collation(
  sqlite4*, 
  const char *zName, 
  int eTextRep, 
  void *pArg,
  int(*xCompare)(void*,int,const void*,int,const void*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDestroy)(void*)
);

/*
** CAPIREF: Collation Needed Callbacks
**
................................................................................
** sqlite4_collation_needed16().  The second argument is the database
** connection.  The third argument is one of [SQLITE4_UTF8], [SQLITE4_UTF16BE],
** or [SQLITE4_UTF16LE], indicating the most desirable form of the collation
** sequence function required.  The fourth parameter is the name of the
** required collation sequence.)^
**
** The callback function should register the desired collation using
** [sqlite4_create_collation()], [sqlite4_create_collation16()], or
** [sqlite4_create_collation_v2()].
*/
int sqlite4_collation_needed(
  sqlite4*, 
  void(*)(void*,sqlite4*,int eTextRep,const char*),
  void* 
);
int sqlite4_collation_needed16(







|







 







<
|







2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
....
2793
2794
2795
2796
2797
2798
2799

2800
2801
2802
2803
2804
2805
2806
2807
** See also:  [sqlite4_collation_needed()] and [sqlite4_collation_needed16()].
*/
int sqlite4_create_collation(
  sqlite4*, 
  const char *zName, 
  int eTextRep, 
  void *pArg,
  int(*xCompare)(void*,int,const void*,int,const void*,int*),
  int(*xMakeKey)(void*,int,const void*,int,void*),
  void(*xDestroy)(void*)
);

/*
** CAPIREF: Collation Needed Callbacks
**
................................................................................
** sqlite4_collation_needed16().  The second argument is the database
** connection.  The third argument is one of [SQLITE4_UTF8], [SQLITE4_UTF16BE],
** or [SQLITE4_UTF16LE], indicating the most desirable form of the collation
** sequence function required.  The fourth parameter is the name of the
** required collation sequence.)^
**
** The callback function should register the desired collation using

** [sqlite4_create_collation()].
*/
int sqlite4_collation_needed(
  sqlite4*, 
  void(*)(void*,sqlite4*,int eTextRep,const char*),
  void* 
);
int sqlite4_collation_needed16(

Changes to src/sqliteInt.h.

1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
** 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, UTF-8 encoded */
  u8 enc;               /* Text encoding handled by xCmp() */
  void *pUser;          /* First argument to xCmp() */
  int (*xCmp)(void*,int, const void*, int, const void*);
  int (*xMkKey)(void*,int, const void*, int, void*);
  void (*xDel)(void*);  /* Destructor for pUser */
};

/*
** A sort order can be either ASC or DESC.
*/







|







1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
** 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, UTF-8 encoded */
  u8 enc;               /* Text encoding handled by xCmp() */
  void *pUser;          /* First argument to xCmp() */
  int (*xCmp)(void*,int, const void*, int, const void*, int *);
  int (*xMkKey)(void*,int, const void*, int, void*);
  void (*xDel)(void*);  /* Destructor for pUser */
};

/*
** A sort order can be either ASC or DESC.
*/

Changes to src/tclsqlite.c.

316
317
318
319
320
321
322
323

324
325
326

327
328
329
330
331
332
333
334

335
336
337
338
339
340
341
** 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->zCmp, -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, TCL_EVAL_DIRECT);
  Tcl_DecrRefCount(pCmd);
  return (atoi(Tcl_GetStringResult(p->interp)));

}

static int tclSqlMkkey(
  void *pCtx,
  int nIn,
  const void *zIn,
  int nOut,







|
>



>





|

|
>







316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
** using TCL script.
*/
static int tclSqlCollate(
  void *pCtx,
  int nA,
  const void *zA,
  int nB,
  const void *zB,
  int *pRes
){
  SqlCollate *p = (SqlCollate *)pCtx;
  Tcl_Obj *pCmd;
  int rc;

  pCmd = Tcl_NewStringObj(p->zCmp, -1);
  Tcl_IncrRefCount(pCmd);
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA));
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB));
  rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
  Tcl_DecrRefCount(pCmd);
  *pRes = (atoi(Tcl_GetStringResult(p->interp)));
  return (rc==TCL_OK ? SQLITE4_OK : SQLITE4_ERROR);
}

static int tclSqlMkkey(
  void *pCtx,
  int nIn,
  const void *zIn,
  int nOut,

Changes to src/vdbe.c.

1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
....
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
    if( affinity ){
      applyAffinity(pIn1, affinity, encoding);
      applyAffinity(pIn3, affinity, encoding);
      if( db->mallocFailed ) goto no_mem;
    }

    assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
    res = sqlite4MemCompare(pIn3, pIn1, pOp->p4.pColl);
  }
  switch( pOp->opcode ){
    case OP_Eq:    res = res==0;     break;
    case OP_Ne:    res = res!=0;     break;
    case OP_Lt:    res = res<0;      break;
    case OP_Le:    res = res<=0;     break;
    case OP_Gt:    res = res>0;      break;
................................................................................
    assert( memIsValid(&aMem[p1+idx]) );
    assert( memIsValid(&aMem[p2+idx]) );
    REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
    REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
    assert( i<pKeyInfo->nField );
    pColl = pKeyInfo->aColl[i];
    bRev = pKeyInfo->aSortOrder[i];
    iCompare = sqlite4MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
    if( iCompare ){
      if( bRev ) iCompare = -iCompare;
      break;
    }
  }
  aPermute = 0;
  break;







|







 







|







1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
....
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
    if( affinity ){
      applyAffinity(pIn1, affinity, encoding);
      applyAffinity(pIn3, affinity, encoding);
      if( db->mallocFailed ) goto no_mem;
    }

    assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
    rc = sqlite4MemCompare(pIn3, pIn1, pOp->p4.pColl, &res);
  }
  switch( pOp->opcode ){
    case OP_Eq:    res = res==0;     break;
    case OP_Ne:    res = res!=0;     break;
    case OP_Lt:    res = res<0;      break;
    case OP_Le:    res = res<=0;     break;
    case OP_Gt:    res = res>0;      break;
................................................................................
    assert( memIsValid(&aMem[p1+idx]) );
    assert( memIsValid(&aMem[p2+idx]) );
    REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
    REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
    assert( i<pKeyInfo->nField );
    pColl = pKeyInfo->aColl[i];
    bRev = pKeyInfo->aSortOrder[i];
    rc = sqlite4MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl, &iCompare);
    if( iCompare ){
      if( bRev ) iCompare = -iCompare;
      break;
    }
  }
  aPermute = 0;
  break;

Changes to src/vdbeInt.h.

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  u8 **pzOut,                  /* Write the resulting key here */
  int *pnOut,                  /* Number of bytes in the key */
  int nExtra                   /* Append extra bytes on end of key */
);
int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v);
int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*);
int sqlite4VdbeShortKey(const u8 *, int, int);
int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite4VdbeExec(Vdbe*);
int sqlite4VdbeList(Vdbe*);
int sqlite4VdbeHalt(Vdbe*);
int sqlite4VdbeChangeEncoding(Mem *, int);
int sqlite4VdbeMemTooBig(Mem*);
int sqlite4VdbeMemCopy(Mem*, const Mem*);
void sqlite4VdbeMemShallowCopy(Mem*, const Mem*, int);







|







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  u8 **pzOut,                  /* Write the resulting key here */
  int *pnOut,                  /* Number of bytes in the key */
  int nExtra                   /* Append extra bytes on end of key */
);
int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v);
int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*);
int sqlite4VdbeShortKey(const u8 *, int, int);
int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*,int*);
int sqlite4VdbeExec(Vdbe*);
int sqlite4VdbeList(Vdbe*);
int sqlite4VdbeHalt(Vdbe*);
int sqlite4VdbeChangeEncoding(Mem *, int);
int sqlite4VdbeMemTooBig(Mem*);
int sqlite4VdbeMemCopy(Mem*, const Mem*);
void sqlite4VdbeMemShallowCopy(Mem*, const Mem*, int);

Changes to src/vdbemem.c.

635
636
637
638
639
640
641
642





643
644
645
646
647
648
649
650
651
652
653
654
655
656

657
658
659
660
661
662
663
664

665


666


667
668
669
670
671
672
673
674
675
676
677


678
679
680
681
682
683
684
685
686
687
688
689
690

691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
** negative, zero or positive if pMem1 is less than, equal to, or greater
** than pMem2. Sorting order is NULL's first, followed by numbers (integers
** and reals) sorted numerically, followed by text ordered by the collating
** sequence pColl and finally blob's ordered by memcmp().
**
** Two NULL values are considered equal by this function.
*/
int sqlite4MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){





  int rc;
  int f1, f2;
  int combined_flags;

  f1 = pMem1->flags;
  f2 = pMem2->flags;
  combined_flags = f1|f2;
  assert( (combined_flags & MEM_RowSet)==0 );
 
  /* If one value is NULL, it is less than the other. If both values
  ** are NULL, return 0.
  */
  if( combined_flags&MEM_Null ){
    return (f2&MEM_Null) - (f1&MEM_Null);

  }

  /* If one value is a number and the other is not, the number is less.
  ** If both are numbers, compare as reals if one is a real, or as integers
  ** if both values are integers.
  */
  if( combined_flags&(MEM_Int|MEM_Real) ){
    if( !(f1&(MEM_Int|MEM_Real)) ) return 1;

    if( !(f2&(MEM_Int|MEM_Real)) ) return -1;


    return (sqlite4_num_compare(pMem1->u.num, pMem2->u.num) - 2);


  }

  /* If one value is a string and the other is a blob, the string is less.
  ** If both are strings, compare using the collating functions.
  */
  if( combined_flags&MEM_Str ){
    if( (f1 & MEM_Str)==0 ){
      return 1;
    }
    if( (f2 & MEM_Str)==0 ){
      return -1;


    }

    assert( pMem1->enc==pMem2->enc );
    assert( pMem1->enc==SQLITE4_UTF8 || 
            pMem1->enc==SQLITE4_UTF16LE || pMem1->enc==SQLITE4_UTF16BE );

    /* The collation sequence must be defined at this point, even if
    ** the user deletes the collation sequence after the vdbe program is
    ** compiled (this was not always the case).
    */
    assert( !pColl || pColl->xCmp );

    if( pColl ){

      if( pMem1->enc==pColl->enc ){
        /* The strings are already in the correct encoding.  Call the
        ** comparison function directly */
        return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
      }else{
        const void *v1, *v2;
        int n1, n2;
        Mem c1;
        Mem c2;
        memset(&c1, 0, sizeof(c1));
        memset(&c2, 0, sizeof(c2));
        sqlite4VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
        sqlite4VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
        v1 = sqlite4ValueText((sqlite4_value*)&c1, pColl->enc);
        n1 = v1==0 ? 0 : c1.n;
        v2 = sqlite4ValueText((sqlite4_value*)&c2, pColl->enc);
        n2 = v2==0 ? 0 : c2.n;
        rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
        sqlite4VdbeMemRelease(&c1);
        sqlite4VdbeMemRelease(&c2);
        return rc;
      }
    }
    /* 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().  */
  rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
  if( rc==0 ){
    rc = pMem1->n - pMem2->n;
  }
  return rc;
}

/* This function is only available internally, it is not part of the
** external API. It works in a similar way to sqlite4_value_text(),
** except the data returned is in the encoding specified by the second







|
>
>
>
>
>
|












|
>







|
>
|
>
>
|
>
>






|
|
|
|
|
>
>













>



|













|










|
|
|







635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
** negative, zero or positive if pMem1 is less than, equal to, or greater
** than pMem2. Sorting order is NULL's first, followed by numbers (integers
** and reals) sorted numerically, followed by text ordered by the collating
** sequence pColl and finally blob's ordered by memcmp().
**
** Two NULL values are considered equal by this function.
*/
int sqlite4MemCompare(
  const Mem *pMem1, 
  const Mem *pMem2, 
  const CollSeq *pColl,
  int *pRes                       /* OUT: Result of comparison operation */
){
  int rc = SQLITE4_OK;
  int f1, f2;
  int combined_flags;

  f1 = pMem1->flags;
  f2 = pMem2->flags;
  combined_flags = f1|f2;
  assert( (combined_flags & MEM_RowSet)==0 );
 
  /* If one value is NULL, it is less than the other. If both values
  ** are NULL, return 0.
  */
  if( combined_flags&MEM_Null ){
    *pRes = (f2&MEM_Null) - (f1&MEM_Null);
    return SQLITE4_OK;
  }

  /* If one value is a number and the other is not, the number is less.
  ** If both are numbers, compare as reals if one is a real, or as integers
  ** if both values are integers.
  */
  if( combined_flags&(MEM_Int|MEM_Real) ){
    if( !(f1&(MEM_Int|MEM_Real)) ){
      *pRes = 1;
    }else if( !(f2&(MEM_Int|MEM_Real)) ){
      *pRes = -1;
    }else{
      *pRes = (sqlite4_num_compare(pMem1->u.num, pMem2->u.num) - 2);
    }
    return SQLITE4_OK;
  }

  /* If one value is a string and the other is a blob, the string is less.
  ** If both are strings, compare using the collating functions.
  */
  if( combined_flags&MEM_Str ){

    if( (f1 & f2 & MEM_Str)==0 ){
      /* This branch is taken if one of the values is not a string. So, if
      ** f1 is a string, then f2 must be a blob. Return -1. Otherwise,
      ** if f2 is a string and f1 is a blob, return +1.  */
      *pRes = (f1 & MEM_Str) ? -1 : +1;
      return SQLITE4_OK;
    }

    assert( pMem1->enc==pMem2->enc );
    assert( pMem1->enc==SQLITE4_UTF8 || 
            pMem1->enc==SQLITE4_UTF16LE || pMem1->enc==SQLITE4_UTF16BE );

    /* The collation sequence must be defined at this point, even if
    ** the user deletes the collation sequence after the vdbe program is
    ** compiled (this was not always the case).
    */
    assert( !pColl || pColl->xCmp );

    if( pColl ){
      void *pUser = pColl->pUser;
      if( pMem1->enc==pColl->enc ){
        /* The strings are already in the correct encoding.  Call the
        ** comparison function directly */
        return pColl->xCmp(pUser, pMem1->n, pMem1->z, pMem2->n, pMem2->z, pRes);
      }else{
        const void *v1, *v2;
        int n1, n2;
        Mem c1;
        Mem c2;
        memset(&c1, 0, sizeof(c1));
        memset(&c2, 0, sizeof(c2));
        sqlite4VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
        sqlite4VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
        v1 = sqlite4ValueText((sqlite4_value*)&c1, pColl->enc);
        n1 = v1==0 ? 0 : c1.n;
        v2 = sqlite4ValueText((sqlite4_value*)&c2, pColl->enc);
        n2 = v2==0 ? 0 : c2.n;
        rc = pColl->xCmp(pUser, n1, v1, n2, v2, pRes);
        sqlite4VdbeMemRelease(&c1);
        sqlite4VdbeMemRelease(&c2);
        return rc;
      }
    }
    /* 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().  */
  *pRes = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
  if( *pRes==0 ){
    *pRes = pMem1->n - pMem2->n;
  }
  return rc;
}

/* This function is only available internally, it is not part of the
** external API. It works in a similar way to sqlite4_value_text(),
** except the data returned is in the encoding specified by the second

Added test/collateerr.test.



























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this test is errors returned by collation sequence comparison
# functions.

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix collateerr

db collate xxx xxx_collate xxx_mkkey

proc xxx_collate {a b} { return [string compare $a $b] }
proc xxx_mkkey {a}     { return $a }

do_execsql_test 1.0 {
  CREATE TABLE t1(a COLLATE xxx, b COLLATE xxx);
  INSERT INTO t1 VALUES('abc', 'def');
  INSERT INTO t1 VALUES('jkl', 'ghi');
}

foreach {tn sql res} {
  1 { SELECT max(a, b) FROM t1 }   {def jkl} 
  2 { SELECT max(a) FROM t1 }      {jkl} 
  3 { SELECT nullif(a,b) FROM t1 } {abc jkl} 
  4 { SELECT a==b FROM t1 }        {0 0}
} {

  proc xxx_collate {a b} { return [string compare $a $b] }
  do_execsql_test 1.$tn.1 $sql $res
   
  proc xxx_collate {a b} {error "no good"}
  do_test 1.$tn.2 { catchsql $sql } {1 {SQL logic error or missing database}}
}

finish_test

Changes to test/test_main.c.

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
....
1382
1383
1384
1385
1386
1387
1388
1389

1390
1391
1392
1393

1394
1395
1396
1397
1398
1399

1400
1401
1402
1403
1404
1405



1406

1407
1408
1409
1410
1411
1412
1413
....
1992
1993
1994
1995
1996
1997
1998
1999

2000
2001
2002
2003
2004
2005
2006
....
2037
2038
2039
2040
2041
2042
2043

2044
2045
2046
2047
2048
2049
2050
2051
....
2152
2153
2154
2155
2156
2157
2158
2159

2160
2161
2162
2163


2164



2165
2166
2167
2168

2169
2170
2171
2172
2173
2174
2175
2176
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  sqlite4 *db;
  Tcl_DString str;
  int rc;
  char *zErr = 0;
  char *zSql;
  char zBuf[30];
  if( argc!=4 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
       " DB FORMAT STRING", 0);
    return TCL_ERROR;
  }
................................................................................
  sqlite4_free(0, (void *)p);
}
static int testCreateCollationCmp(
  void *pCtx,
  int nLeft,
  const void *zLeft,
  int nRight,
  const void *zRight

){
  TestCollationX *p = (TestCollationX *)pCtx;
  Tcl_Obj *pScript = Tcl_DuplicateObj(p->pCmp);
  int iRes = 0;


  Tcl_IncrRefCount(pScript);
  Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zLeft, nLeft));
  Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zRight,nRight));

  if( TCL_OK!=Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL)

   || TCL_OK!=Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iRes)
  ){
    Tcl_BackgroundError(p->interp);
  }
  Tcl_DecrRefCount(pScript);




  return iRes;

}
static int test_create_collation(
  ClientData clientData, /* Not used */
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
................................................................................
** interp pointer to use when evaluating the TCL script is stored in
** pTestCollateInterp.
*/
static Tcl_Interp* pTestCollateInterp;
static int test_collate_func(
  void *pCtx, 
  int nA, const void *zA,
  int nB, const void *zB

){
  Tcl_Interp *i = pTestCollateInterp;
  int encin = SQLITE4_PTR_TO_INT(pCtx);
  int res;
  int n;
  sqlite4_env *pEnv = sqlite4_context_env(pCtx);

................................................................................
    sqlite4ValueFree(pVal);
  }
  sqlite4EndBenignMalloc(pEnv);

  Tcl_EvalObjEx(i, pX, 0);
  Tcl_DecrRefCount(pX);
  Tcl_GetIntFromObj(i, Tcl_GetObjResult(i), &res);

  return res;
}
static int test_collate(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
................................................................................
** Both collating functions increment the unaligned utf16 counter
** whenever they see a string that begins on an odd byte boundary.
*/
static int unaligned_string_counter = 0;
static int alignmentCollFunc(
  void *NotUsed,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2

){
  int rc, n;
  n = nKey1<nKey2 ? nKey1 : nKey2;
  if( nKey1>0 && 1==(1&(SQLITE4_PTR_TO_INT(pKey1))) ) unaligned_string_counter++;


  if( nKey2>0 && 1==(1&(SQLITE4_PTR_TO_INT(pKey2))) ) unaligned_string_counter++;



  rc = memcmp(pKey1, pKey2, n);
  if( rc==0 ){
    rc = nKey1 - nKey2;
  }

  return rc;
}
static int add_alignment_test_collations(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){







<







 







|
>




>





|
>
|
<
<



>
>
>
|
>







 







|
>







 







>
|







 







|
>

|

|
>
>
|
>
>
>
|
|
|

>
|







327
328
329
330
331
332
333

334
335
336
337
338
339
340
....
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402


1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
....
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
....
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
....
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  sqlite4 *db;
  Tcl_DString str;
  int rc;

  char *zSql;
  char zBuf[30];
  if( argc!=4 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
       " DB FORMAT STRING", 0);
    return TCL_ERROR;
  }
................................................................................
  sqlite4_free(0, (void *)p);
}
static int testCreateCollationCmp(
  void *pCtx,
  int nLeft,
  const void *zLeft,
  int nRight,
  const void *zRight,
  int *pRes
){
  TestCollationX *p = (TestCollationX *)pCtx;
  Tcl_Obj *pScript = Tcl_DuplicateObj(p->pCmp);
  int iRes = 0;
  int rc;

  Tcl_IncrRefCount(pScript);
  Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zLeft, nLeft));
  Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj((char *)zRight,nRight));

  rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
  if( rc==TCL_OK ){
   rc = Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iRes);


  }
  Tcl_DecrRefCount(pScript);

  if( rc!=TCL_OK ){
    return SQLITE4_ERROR;
  }
  *pRes = iRes;
  return SQLITE4_OK;
}
static int test_create_collation(
  ClientData clientData, /* Not used */
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
................................................................................
** interp pointer to use when evaluating the TCL script is stored in
** pTestCollateInterp.
*/
static Tcl_Interp* pTestCollateInterp;
static int test_collate_func(
  void *pCtx, 
  int nA, const void *zA,
  int nB, const void *zB,
  int *pRes
){
  Tcl_Interp *i = pTestCollateInterp;
  int encin = SQLITE4_PTR_TO_INT(pCtx);
  int res;
  int n;
  sqlite4_env *pEnv = sqlite4_context_env(pCtx);

................................................................................
    sqlite4ValueFree(pVal);
  }
  sqlite4EndBenignMalloc(pEnv);

  Tcl_EvalObjEx(i, pX, 0);
  Tcl_DecrRefCount(pX);
  Tcl_GetIntFromObj(i, Tcl_GetObjResult(i), &res);
  *pRes = res;
  return SQLITE4_OK;
}
static int test_collate(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
................................................................................
** Both collating functions increment the unaligned utf16 counter
** whenever they see a string that begins on an odd byte boundary.
*/
static int unaligned_string_counter = 0;
static int alignmentCollFunc(
  void *NotUsed,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2,
  int *pRes
){
  int res, n;
  n = nKey1<nKey2 ? nKey1 : nKey2;
  if( nKey1>0 && 1==(1&(SQLITE4_PTR_TO_INT(pKey1))) ){
    unaligned_string_counter++;
  }
  if( nKey2>0 && 1==(1&(SQLITE4_PTR_TO_INT(pKey2))) ){
    unaligned_string_counter++;
  }

  res = memcmp(pKey1, pKey2, n);
  if( res==0 ){
    res = nKey1 - nKey2;
  }
  *pRes = res;
  return SQLITE4_OK;
}
static int add_alignment_test_collations(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){