/ Check-in [0bf15505]
Login

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

Overview
Comment:Add scalar function "prefix_length()" to ext/misc/prefixes.c.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0bf1550507d9d3c8a41f8a50db3a59bf808f0a4e24637dc0905d35579305eca7
User & Date: dan 2019-01-14 20:44:00
Context
2019-01-15
14:31
Fix a problem triggered by DELETE statements with WHERE clauses that use the OR-optimization on some virtual tables. check-in: ecf5caa7 user: dan tags: trunk
2019-01-14
20:44
Add scalar function "prefix_length()" to ext/misc/prefixes.c. check-in: 0bf15505 user: dan tags: trunk
19:13
Fix a problem causing some Tcl test cases to fail with errors like "expected: [<multiline-whitespace>], got: []". check-in: 11b8a4cb user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

   448    448     $(TOP)/ext/fts5/fts5_test_mi.c \
   449    449     $(TOP)/ext/fts5/fts5_test_tok.c \
   450    450     $(TOP)/ext/misc/ieee754.c \
   451    451     $(TOP)/ext/misc/mmapwarm.c \
   452    452     $(TOP)/ext/misc/nextchar.c \
   453    453     $(TOP)/ext/misc/normalize.c \
   454    454     $(TOP)/ext/misc/percentile.c \
          455  +  $(TOP)/ext/misc/prefixes.c \
   455    456     $(TOP)/ext/misc/regexp.c \
   456    457     $(TOP)/ext/misc/remember.c \
   457    458     $(TOP)/ext/misc/series.c \
   458    459     $(TOP)/ext/misc/spellfix.c \
   459    460     $(TOP)/ext/misc/totype.c \
   460    461     $(TOP)/ext/misc/unionvtab.c \
   461    462     $(TOP)/ext/misc/wholenumber.c \

Changes to Makefile.msc.

  1523   1523     $(TOP)\ext\fts5\fts5_test_mi.c \
  1524   1524     $(TOP)\ext\fts5\fts5_test_tok.c \
  1525   1525     $(TOP)\ext\misc\ieee754.c \
  1526   1526     $(TOP)\ext\misc\mmapwarm.c \
  1527   1527     $(TOP)\ext\misc\nextchar.c \
  1528   1528     $(TOP)\ext\misc\normalize.c \
  1529   1529     $(TOP)\ext\misc\percentile.c \
         1530  +  $(TOP)\ext\misc\prefixes.c \
  1530   1531     $(TOP)\ext\misc\regexp.c \
  1531   1532     $(TOP)\ext\misc\remember.c \
  1532   1533     $(TOP)\ext\misc\series.c \
  1533   1534     $(TOP)\ext\misc\spellfix.c \
  1534   1535     $(TOP)\ext\misc\totype.c \
  1535   1536     $(TOP)\ext\misc\unionvtab.c \
  1536   1537     $(TOP)\ext\misc\wholenumber.c

Changes to ext/misc/prefixes.c.

   245    245     /* xRename     */ 0,
   246    246     /* xSavepoint  */ 0,
   247    247     /* xRelease    */ 0,
   248    248     /* xRollbackTo */ 0,
   249    249     /* xShadowName */ 0
   250    250   };
   251    251   
          252  +/*
          253  +** This is a copy of the SQLITE_SKIP_UTF8(zIn) macro in sqliteInt.h.
          254  +**
          255  +** Assuming zIn points to the first byte of a UTF-8 character,
          256  +** advance zIn to point to the first byte of the next UTF-8 character.
          257  +*/
          258  +#define PREFIX_SKIP_UTF8(zIn) {                        \
          259  +  if( (*(zIn++))>=0xc0 ){                              \
          260  +    while( (*zIn & 0xc0)==0x80 ){ zIn++; }             \
          261  +  }                                                    \
          262  +}
          263  +
          264  +/*
          265  +** Implementation of function prefix_length(). This function accepts two
          266  +** strings as arguments and returns the length in characters (not bytes), 
          267  +** of the longest prefix shared by the two strings. For example:
          268  +**
          269  +**   prefix_length('abcdxxx', 'abcyy') == 3
          270  +**   prefix_length('abcdxxx', 'bcyyy') == 0
          271  +**   prefix_length('abcdxxx', 'ab')    == 2
          272  +**   prefix_length('ab',      'abcd')  == 2
          273  +**
          274  +** This function assumes the input is well-formed utf-8. If it is not,
          275  +** it is possible for this function to return -1.
          276  +*/
          277  +static void prefixLengthFunc(
          278  +  sqlite3_context *ctx,
          279  +  int nVal,
          280  +  sqlite3_value **apVal
          281  +){
          282  +  int nByte;                      /* Number of bytes to compare */
          283  +  int nRet = 0;                   /* Return value */
          284  +  const unsigned char *zL = sqlite3_value_text(apVal[0]);
          285  +  const unsigned char *zR = sqlite3_value_text(apVal[1]);
          286  +  int nL = sqlite3_value_bytes(apVal[0]);
          287  +  int nR = sqlite3_value_bytes(apVal[1]);
          288  +  int i;
          289  +
          290  +  nByte = (nL > nR ? nL : nR);
          291  +  for(i=0; i<nByte; i++){
          292  +    if( zL[i]!=zR[i] ) break;
          293  +    if( (zL[i] & 0xC0)!=0x80 ) nRet++;
          294  +  }
          295  +
          296  +  if( (zL[i] & 0xC0)==0x80 ) nRet--;
          297  +  sqlite3_result_int(ctx, nRet);
          298  +}
   252    299   
   253    300   #ifdef _WIN32
   254    301   __declspec(dllexport)
   255    302   #endif
   256    303   int sqlite3_prefixes_init(
   257    304     sqlite3 *db, 
   258    305     char **pzErrMsg, 
   259    306     const sqlite3_api_routines *pApi
   260    307   ){
   261    308     int rc = SQLITE_OK;
   262    309     SQLITE_EXTENSION_INIT2(pApi);
   263    310     rc = sqlite3_create_module(db, "prefixes", &prefixesModule, 0);
          311  +  if( rc==SQLITE_OK ){
          312  +    rc = sqlite3_create_function(
          313  +        db, "prefix_length", 2, SQLITE_UTF8, 0, prefixLengthFunc, 0, 0
          314  +    );
          315  +  }
   264    316     return rc;
   265    317   }

Changes to main.mk.

   365    365     $(TOP)/ext/misc/fileio.c \
   366    366     $(TOP)/ext/misc/fuzzer.c \
   367    367     $(TOP)/ext/misc/ieee754.c \
   368    368     $(TOP)/ext/misc/mmapwarm.c \
   369    369     $(TOP)/ext/misc/nextchar.c \
   370    370     $(TOP)/ext/misc/normalize.c \
   371    371     $(TOP)/ext/misc/percentile.c \
          372  +  $(TOP)/ext/misc/prefixes.c \
   372    373     $(TOP)/ext/misc/regexp.c \
   373    374     $(TOP)/ext/misc/remember.c \
   374    375     $(TOP)/ext/misc/series.c \
   375    376     $(TOP)/ext/misc/spellfix.c \
   376    377     $(TOP)/ext/misc/totype.c \
   377    378     $(TOP)/ext/misc/unionvtab.c \
   378    379     $(TOP)/ext/misc/wholenumber.c \

Changes to src/test1.c.

  7137   7137     extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*);
  7138   7138     extern int sqlite3_explain_init(sqlite3*,char**,const sqlite3_api_routines*);
  7139   7139     extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
  7140   7140     extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*);
  7141   7141     extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*);
  7142   7142     extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*);
  7143   7143     extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
         7144  +  extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*);
  7144   7145     extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
  7145   7146     extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*);
  7146   7147     extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
  7147   7148     extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
  7148   7149     extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
  7149   7150     extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
  7150   7151     extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
................................................................................
  7162   7163       { "eval",                  sqlite3_eval_init                 },
  7163   7164       { "explain",               sqlite3_explain_init              },
  7164   7165       { "fileio",                sqlite3_fileio_init               },
  7165   7166       { "fuzzer",                sqlite3_fuzzer_init               },
  7166   7167       { "ieee754",               sqlite3_ieee_init                 },
  7167   7168       { "nextchar",              sqlite3_nextchar_init             },
  7168   7169       { "percentile",            sqlite3_percentile_init           },
         7170  +    { "prefixes",              sqlite3_prefixes_init             },
  7169   7171       { "regexp",                sqlite3_regexp_init               },
  7170   7172       { "remember",              sqlite3_remember_init             },
  7171   7173       { "series",                sqlite3_series_init               },
  7172   7174       { "spellfix",              sqlite3_spellfix_init             },
  7173   7175       { "totype",                sqlite3_totype_init               },
  7174   7176       { "unionvtab",             sqlite3_unionvtab_init            },
  7175   7177       { "wholenumber",           sqlite3_wholenumber_init          },

Added test/prefixes.test.

            1  +# 2018-01-15
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this file is prefixes.c extension
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix unionvtab
           18  +
           19  +ifcapable !vtab {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +load_static_extension db prefixes
           25  +
           26  +foreach {tn zLeft zRight expected} {
           27  +  1 abcdxxx abcyy    3
           28  +  2 abcdxxx bcyyy    0
           29  +  3 abcdxxx ab       2
           30  +  4 ab      abcd     2
           31  +
           32  +  5 "xyz\u1234xz" "xyz\u1234xy" 5
           33  +  6 "xyz\u1234"   "xyz\u1234xy" 4
           34  +  7 "xyz\u1234"   "xyz\u1234"   4
           35  +  8 "xyz\u1234xy" "xyz\u1234"   4
           36  +  9 "xyz\u1234xy" "xyz\u1233"   3
           37  + 10 "xyz\u1234xy" "xyz\u1235"   3
           38  +} {
           39  +  do_execsql_test 1.$tn { SELECT prefix_length($zLeft, $zRight) } $expected
           40  +}
           41  +
           42  +
           43  +do_execsql_test 2.0 {
           44  +  CREATE TABLE t1(k TEXT UNIQUE, v INTEGER);
           45  +  INSERT INTO t1 VALUES
           46  +    ('aback', 1),
           47  +    ('abaft', 2),
           48  +    ('abandon', 3),
           49  +    ('abandoned', 4),
           50  +    ('abandoning', 5),
           51  +    ('abandonment', 6),
           52  +    ('abandons', 7),
           53  +    ('abase', 8),
           54  +    ('abased', 9),
           55  +    ('abasement', 10),
           56  +    ('abasements', 11),
           57  +    ('abases', 12),
           58  +    ('abash', 13),
           59  +    ('abashed', 14),
           60  +    ('abashes', 15),
           61  +    ('abashing', 16),
           62  +    ('abasing', 17),
           63  +    ('abate', 18),
           64  +    ('abated', 19),
           65  +    ('abatement', 20),
           66  +    ('abatements', 21);
           67  +}
           68  +
           69  +foreach {tn INPUT expected} {
           70  +  1 abatementt   abatement
           71  +  2 abashet      abash
           72  +  3 abandonio    abandon
           73  +  4 abasemenu    abase
           74  +} {
           75  +  do_execsql_test 2.$tn {
           76  +    WITH finder(str) AS (
           77  +      SELECT (SELECT max(k) FROM t1 WHERE k<=$INPUT)
           78  +        UNION ALL
           79  +        SELECT (
           80  +          SELECT max(k) FROM t1 
           81  +          WHERE k<=substr($INPUT, 1, prefix_length(finder.str, $INPUT))
           82  +        ) FROM finder WHERE length(finder.str)>0
           83  +      )
           84  +    SELECT str FROM finder WHERE length(str)==prefix_length(str, $INPUT) LIMIT 1
           85  +  } $expected
           86  +}
           87  +
           88  +finish_test