14492 lines
466 KiB
Diff
14492 lines
466 KiB
Diff
--- origsrc/sqlite-autoconf-3080802/Makefile.am 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/Makefile.am 2015-01-31 00:31:56.318138700 +0100
|
||
@@ -1,14 +1,58 @@
|
||
|
||
-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
|
||
+AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DUSE_SYSTEM_SQLITE=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE -DSQLITE_WIN32_MAX_PATH_BYTES=2048 -DSQLITE_MAX_PATH_LENGTH=2048
|
||
|
||
-lib_LTLIBRARIES = libsqlite3.la
|
||
+lib_LTLIBRARIES = libsqlite3.la libsqlite3icu.la libsqlite3zlib.la \
|
||
+ libsqlite3eval.la \
|
||
+ libsqlite3vfslog.la libsqlite3vtshim.la libsqlite3amatch.la \
|
||
+ libsqlite3closure.la libsqlite3fuzzer.la libsqlite3ieee754.la \
|
||
+ libsqlite3nextchar.la libsqlite3percentile.la libsqlite3regexp.la \
|
||
+ libsqlite3rot13.la libsqlite3spellfix.la libsqlite3totype.la \
|
||
+ libsqlite3wholenumber.la libsqlite3compress.la libsqlite3fileio.la
|
||
libsqlite3_la_SOURCES = sqlite3.c
|
||
-libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
|
||
+libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3icu_la_SOURCES = sqlite3icu.c
|
||
+libsqlite3icu_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc -licui18n -licuuc
|
||
+libsqlite3zlib_la_SOURCES = zlib.c
|
||
+libsqlite3zlib_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc -lz
|
||
+libsqlite3eval_la_SOURCES = eval.c
|
||
+libsqlite3eval_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3compress_la_SOURCES = compress.c
|
||
+libsqlite3compress_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc -lz
|
||
+libsqlite3vfslog_la_SOURCES = vfslog.c
|
||
+libsqlite3vfslog_la_LIBADD = $(top_builddir)/libsqlite3.la
|
||
+libsqlite3vfslog_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3vfslog_la_DEPENDENCIES = $(top_builddir)/libsqlite3.la
|
||
+libsqlite3vtshim_la_SOURCES = vtshim.c
|
||
+libsqlite3vtshim_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3amatch_la_SOURCES = amatch.c
|
||
+libsqlite3amatch_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3closure_la_SOURCES = closure.c
|
||
+libsqlite3closure_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3fileio_la_SOURCES = fileio.c
|
||
+libsqlite3fileio_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3fuzzer_la_SOURCES = fuzzer.c
|
||
+libsqlite3fuzzer_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3ieee754_la_SOURCES = ieee754.c
|
||
+libsqlite3ieee754_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3nextchar_la_SOURCES = nextchar.c
|
||
+libsqlite3nextchar_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3percentile_la_SOURCES = percentile.c
|
||
+libsqlite3percentile_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3regexp_la_SOURCES = regexp.c
|
||
+libsqlite3regexp_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3rot13_la_SOURCES = rot13.c
|
||
+libsqlite3rot13_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3spellfix_la_SOURCES = spellfix.c
|
||
+libsqlite3spellfix_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3totype_la_SOURCES = totype.c
|
||
+libsqlite3totype_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
+libsqlite3wholenumber_la_SOURCES = wholenumber.c
|
||
+libsqlite3wholenumber_la_LDFLAGS = -no-undefined -version-info 8:6:8 -static-libgcc
|
||
|
||
bin_PROGRAMS = sqlite3
|
||
sqlite3_SOURCES = shell.c sqlite3.h
|
||
-sqlite3_LDADD = $(top_builddir)/libsqlite3.la @READLINE_LIBS@
|
||
-sqlite3_DEPENDENCIES = $(top_builddir)/libsqlite3.la
|
||
+sqlite3_LDADD = $(top_builddir)/libsqlite3vfslog.la $(top_builddir)/libsqlite3.la @READLINE_LIBS@
|
||
+sqlite3_DEPENDENCIES = $(top_builddir)/libsqlite3vfslog.la $(top_builddir)/libsqlite3.la
|
||
|
||
include_HEADERS = sqlite3.h sqlite3ext.h
|
||
|
||
--- origsrc/sqlite-autoconf-3080802/amatch.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/amatch.c 2015-01-31 00:31:56.334139600 +0100
|
||
@@ -0,0 +1,1502 @@
|
||
+/*
|
||
+** 2013-03-14
|
||
+**
|
||
+** 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 contains code for a demonstration virtual table that finds
|
||
+** "approximate matches" - strings from a finite set that are nearly the
|
||
+** same as a single input string. The virtual table is called "amatch".
|
||
+**
|
||
+** A amatch virtual table is created like this:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE f USING approximate_match(
|
||
+** vocabulary_table=<tablename>, -- V
|
||
+** vocabulary_word=<columnname>, -- W
|
||
+** vocabulary_language=<columnname>, -- L
|
||
+** edit_distances=<edit-cost-table>
|
||
+** );
|
||
+**
|
||
+** When it is created, the new amatch table must be supplied with the
|
||
+** the name of a table V and columns V.W and V.L such that
|
||
+**
|
||
+** SELECT W FROM V WHERE L=$language
|
||
+**
|
||
+** returns the allowed vocabulary for the match. If the "vocabulary_language"
|
||
+** or L columnname is left unspecified or is an empty string, then no
|
||
+** filtering of the vocabulary by language is performed.
|
||
+**
|
||
+** For efficiency, it is essential that the vocabulary table be indexed:
|
||
+**
|
||
+** CREATE vocab_index ON V(W)
|
||
+**
|
||
+** A separate edit-cost-table provides scoring information that defines
|
||
+** what it means for one string to be "close" to another.
|
||
+**
|
||
+** The edit-cost-table must contain exactly four columns (more precisely,
|
||
+** the statement "SELECT * FROM <edit-cost-table>" must return records
|
||
+** that consist of four columns). It does not matter what the columns are
|
||
+** named.
|
||
+**
|
||
+** Each row in the edit-cost-table represents a single character
|
||
+** transformation going from user input to the vocabulary. The leftmost
|
||
+** column of the row (column 0) contains an integer identifier of the
|
||
+** language to which the transformation rule belongs (see "MULTIPLE LANGUAGES"
|
||
+** below). The second column of the row (column 1) contains the input
|
||
+** character or characters - the characters of user input. The third
|
||
+** column contains characters as they appear in the vocabulary table.
|
||
+** And the fourth column contains the integer cost of making the
|
||
+** transformation. For example:
|
||
+**
|
||
+** CREATE TABLE f_data(iLang, cFrom, cTo, Cost);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
|
||
+**
|
||
+** The first row inserted into the edit-cost-table by the SQL script
|
||
+** above indicates that the cost of having an extra 'a' in the vocabulary
|
||
+** table that is missing in the user input 100. (All costs are integers.
|
||
+** Overall cost must not exceed 16777216.) The second INSERT statement
|
||
+** creates a rule saying that the cost of having a single letter 'b' in
|
||
+** user input which is missing in the vocabulary table is 87. The third
|
||
+** INSERT statement mean that the cost of matching an 'o' in user input
|
||
+** against an 'oe' in the vocabulary table is 38. And so forth.
|
||
+**
|
||
+** The following rules are special:
|
||
+**
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '', 97);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '', '?', 98);
|
||
+** INSERT INTO f_data(iLang, cFrom, cTo, Cost) VALUES(0, '?', '?', 99);
|
||
+**
|
||
+** The '?' to '' rule is the cost of having any single character in the input
|
||
+** that is not found in the vocabular. The '' to '?' rule is the cost of
|
||
+** having a character in the vocabulary table that is missing from input.
|
||
+** And the '?' to '?' rule is the cost of doing an arbitrary character
|
||
+** substitution. These three generic rules apply across all languages.
|
||
+** In other words, the iLang field is ignored for the generic substitution
|
||
+** rules. If more than one cost is given for a generic substitution rule,
|
||
+** then the lowest cost is used.
|
||
+**
|
||
+** Once it has been created, the amatch virtual table can be queried
|
||
+** as follows:
|
||
+**
|
||
+** SELECT word, distance FROM f
|
||
+** WHERE word MATCH 'abcdefg'
|
||
+** AND distance<200;
|
||
+**
|
||
+** This query outputs the strings contained in the T(F) field that
|
||
+** are close to "abcdefg" and in order of increasing distance. No string
|
||
+** is output more than once. If there are multiple ways to transform the
|
||
+** target string ("abcdefg") into a string in the vocabulary table then
|
||
+** the lowest cost transform is the one that is returned. In this example,
|
||
+** the search is limited to strings with a total distance of less than 200.
|
||
+**
|
||
+** For efficiency, it is important to put tight bounds on the distance.
|
||
+** The time and memory space needed to perform this query is exponential
|
||
+** in the maximum distance. A good rule of thumb is to limit the distance
|
||
+** to no more than 1.5 or 2 times the maximum cost of any rule in the
|
||
+** edit-cost-table.
|
||
+**
|
||
+** The amatch is a read-only table. Any attempt to DELETE, INSERT, or
|
||
+** UPDATE on a amatch table will throw an error.
|
||
+**
|
||
+** It is important to put some kind of a limit on the amatch output. This
|
||
+** can be either in the form of a LIMIT clause at the end of the query,
|
||
+** or better, a "distance<NNN" constraint where NNN is some number. The
|
||
+** running time and memory requirement is exponential in the value of NNN
|
||
+** so you want to make sure that NNN is not too big. A value of NNN that
|
||
+** is about twice the average transformation cost seems to give good results.
|
||
+**
|
||
+** The amatch table can be useful for tasks such as spelling correction.
|
||
+** Suppose all allowed words are in table vocabulary(w). Then one would create
|
||
+** an amatch virtual table like this:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE ex1 USING amatch(
|
||
+** vocabtable=vocabulary,
|
||
+** vocabcolumn=w,
|
||
+** edit_distances=ec1
|
||
+** );
|
||
+**
|
||
+** Then given an input word $word, look up close spellings this way:
|
||
+**
|
||
+** SELECT word, distance FROM ex1
|
||
+** WHERE word MATCH $word AND distance<200;
|
||
+**
|
||
+** MULTIPLE LANGUAGES
|
||
+**
|
||
+** Normally, the "iLang" value associated with all character transformations
|
||
+** in the edit-cost-table is zero. However, if required, the amatch
|
||
+** virtual table allows multiple languages to be defined. Each query uses
|
||
+** only a single iLang value. This allows, for example, a single
|
||
+** amatch table to support multiple languages.
|
||
+**
|
||
+** By default, only the rules with iLang=0 are used. To specify an
|
||
+** alternative language, a "language = ?" expression must be added to the
|
||
+** WHERE clause of a SELECT, where ? is the integer identifier of the desired
|
||
+** language. For example:
|
||
+**
|
||
+** SELECT word, distance FROM ex1
|
||
+** WHERE word MATCH $word
|
||
+** AND distance<=200
|
||
+** AND language=1 -- Specify use language 1 instead of 0
|
||
+**
|
||
+** If no "language = ?" constraint is specified in the WHERE clause, language
|
||
+** 0 is used.
|
||
+**
|
||
+** LIMITS
|
||
+**
|
||
+** The maximum language number is 2147483647. The maximum length of either
|
||
+** of the strings in the second or third column of the amatch data table
|
||
+** is 50 bytes. The maximum cost on a rule is 1000.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <assert.h>
|
||
+#include <stdio.h>
|
||
+#include <ctype.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+/*
|
||
+** Forward declaration of objects used by this implementation
|
||
+*/
|
||
+typedef struct amatch_vtab amatch_vtab;
|
||
+typedef struct amatch_cursor amatch_cursor;
|
||
+typedef struct amatch_rule amatch_rule;
|
||
+typedef struct amatch_word amatch_word;
|
||
+typedef struct amatch_avl amatch_avl;
|
||
+
|
||
+
|
||
+/*****************************************************************************
|
||
+** AVL Tree implementation
|
||
+*/
|
||
+/*
|
||
+** Objects that want to be members of the AVL tree should embedded an
|
||
+** instance of this structure.
|
||
+*/
|
||
+struct amatch_avl {
|
||
+ amatch_word *pWord; /* Points to the object being stored in the tree */
|
||
+ char *zKey; /* Key. zero-terminated string. Must be unique */
|
||
+ amatch_avl *pBefore; /* Other elements less than zKey */
|
||
+ amatch_avl *pAfter; /* Other elements greater than zKey */
|
||
+ amatch_avl *pUp; /* Parent element */
|
||
+ short int height; /* Height of this node. Leaf==1 */
|
||
+ short int imbalance; /* Height difference between pBefore and pAfter */
|
||
+};
|
||
+
|
||
+/* Recompute the amatch_avl.height and amatch_avl.imbalance fields for p.
|
||
+** Assume that the children of p have correct heights.
|
||
+*/
|
||
+static void amatchAvlRecomputeHeight(amatch_avl *p){
|
||
+ short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
||
+ short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
||
+ p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
||
+ p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** P B
|
||
+** / \ / \
|
||
+** B Z ==> X P
|
||
+** / \ / \
|
||
+** X Y Y Z
|
||
+**
|
||
+*/
|
||
+static amatch_avl *amatchAvlRotateBefore(amatch_avl *pP){
|
||
+ amatch_avl *pB = pP->pBefore;
|
||
+ amatch_avl *pY = pB->pAfter;
|
||
+ pB->pUp = pP->pUp;
|
||
+ pB->pAfter = pP;
|
||
+ pP->pUp = pB;
|
||
+ pP->pBefore = pY;
|
||
+ if( pY ) pY->pUp = pP;
|
||
+ amatchAvlRecomputeHeight(pP);
|
||
+ amatchAvlRecomputeHeight(pB);
|
||
+ return pB;
|
||
+}
|
||
+
|
||
+/*
|
||
+** P A
|
||
+** / \ / \
|
||
+** X A ==> P Z
|
||
+** / \ / \
|
||
+** Y Z X Y
|
||
+**
|
||
+*/
|
||
+static amatch_avl *amatchAvlRotateAfter(amatch_avl *pP){
|
||
+ amatch_avl *pA = pP->pAfter;
|
||
+ amatch_avl *pY = pA->pBefore;
|
||
+ pA->pUp = pP->pUp;
|
||
+ pA->pBefore = pP;
|
||
+ pP->pUp = pA;
|
||
+ pP->pAfter = pY;
|
||
+ if( pY ) pY->pUp = pP;
|
||
+ amatchAvlRecomputeHeight(pP);
|
||
+ amatchAvlRecomputeHeight(pA);
|
||
+ return pA;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return a pointer to the pBefore or pAfter pointer in the parent
|
||
+** of p that points to p. Or if p is the root node, return pp.
|
||
+*/
|
||
+static amatch_avl **amatchAvlFromPtr(amatch_avl *p, amatch_avl **pp){
|
||
+ amatch_avl *pUp = p->pUp;
|
||
+ if( pUp==0 ) return pp;
|
||
+ if( pUp->pAfter==p ) return &pUp->pAfter;
|
||
+ return &pUp->pBefore;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Rebalance all nodes starting with p and working up to the root.
|
||
+** Return the new root.
|
||
+*/
|
||
+static amatch_avl *amatchAvlBalance(amatch_avl *p){
|
||
+ amatch_avl *pTop = p;
|
||
+ amatch_avl **pp;
|
||
+ while( p ){
|
||
+ amatchAvlRecomputeHeight(p);
|
||
+ if( p->imbalance>=2 ){
|
||
+ amatch_avl *pB = p->pBefore;
|
||
+ if( pB->imbalance<0 ) p->pBefore = amatchAvlRotateAfter(pB);
|
||
+ pp = amatchAvlFromPtr(p,&p);
|
||
+ p = *pp = amatchAvlRotateBefore(p);
|
||
+ }else if( p->imbalance<=(-2) ){
|
||
+ amatch_avl *pA = p->pAfter;
|
||
+ if( pA->imbalance>0 ) p->pAfter = amatchAvlRotateBefore(pA);
|
||
+ pp = amatchAvlFromPtr(p,&p);
|
||
+ p = *pp = amatchAvlRotateAfter(p);
|
||
+ }
|
||
+ pTop = p;
|
||
+ p = p->pUp;
|
||
+ }
|
||
+ return pTop;
|
||
+}
|
||
+
|
||
+/* Search the tree rooted at p for an entry with zKey. Return a pointer
|
||
+** to the entry or return NULL.
|
||
+*/
|
||
+static amatch_avl *amatchAvlSearch(amatch_avl *p, const char *zKey){
|
||
+ int c;
|
||
+ while( p && (c = strcmp(zKey, p->zKey))!=0 ){
|
||
+ p = (c<0) ? p->pBefore : p->pAfter;
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
+
|
||
+/* Find the first node (the one with the smallest key).
|
||
+*/
|
||
+static amatch_avl *amatchAvlFirst(amatch_avl *p){
|
||
+ if( p ) while( p->pBefore ) p = p->pBefore;
|
||
+ return p;
|
||
+}
|
||
+
|
||
+#if 0 /* NOT USED */
|
||
+/* Return the node with the next larger key after p.
|
||
+*/
|
||
+static amatch_avl *amatchAvlNext(amatch_avl *p){
|
||
+ amatch_avl *pPrev = 0;
|
||
+ while( p && p->pAfter==pPrev ){
|
||
+ pPrev = p;
|
||
+ p = p->pUp;
|
||
+ }
|
||
+ if( p && pPrev==0 ){
|
||
+ p = amatchAvlFirst(p->pAfter);
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
+#endif
|
||
+
|
||
+#if 0 /* NOT USED */
|
||
+/* Verify AVL tree integrity
|
||
+*/
|
||
+static int amatchAvlIntegrity(amatch_avl *pHead){
|
||
+ amatch_avl *p;
|
||
+ if( pHead==0 ) return 1;
|
||
+ if( (p = pHead->pBefore)!=0 ){
|
||
+ assert( p->pUp==pHead );
|
||
+ assert( amatchAvlIntegrity(p) );
|
||
+ assert( strcmp(p->zKey, pHead->zKey)<0 );
|
||
+ while( p->pAfter ) p = p->pAfter;
|
||
+ assert( strcmp(p->zKey, pHead->zKey)<0 );
|
||
+ }
|
||
+ if( (p = pHead->pAfter)!=0 ){
|
||
+ assert( p->pUp==pHead );
|
||
+ assert( amatchAvlIntegrity(p) );
|
||
+ assert( strcmp(p->zKey, pHead->zKey)>0 );
|
||
+ p = amatchAvlFirst(p);
|
||
+ assert( strcmp(p->zKey, pHead->zKey)>0 );
|
||
+ }
|
||
+ return 1;
|
||
+}
|
||
+static int amatchAvlIntegrity2(amatch_avl *pHead){
|
||
+ amatch_avl *p, *pNext;
|
||
+ for(p=amatchAvlFirst(pHead); p; p=pNext){
|
||
+ pNext = amatchAvlNext(p);
|
||
+ if( pNext==0 ) break;
|
||
+ assert( strcmp(p->zKey, pNext->zKey)<0 );
|
||
+ }
|
||
+ return 1;
|
||
+}
|
||
+#endif
|
||
+
|
||
+/* Insert a new node pNew. Return NULL on success. If the key is not
|
||
+** unique, then do not perform the insert but instead leave pNew unchanged
|
||
+** and return a pointer to an existing node with the same key.
|
||
+*/
|
||
+static amatch_avl *amatchAvlInsert(amatch_avl **ppHead, amatch_avl *pNew){
|
||
+ int c;
|
||
+ amatch_avl *p = *ppHead;
|
||
+ if( p==0 ){
|
||
+ p = pNew;
|
||
+ pNew->pUp = 0;
|
||
+ }else{
|
||
+ while( p ){
|
||
+ c = strcmp(pNew->zKey, p->zKey);
|
||
+ if( c<0 ){
|
||
+ if( p->pBefore ){
|
||
+ p = p->pBefore;
|
||
+ }else{
|
||
+ p->pBefore = pNew;
|
||
+ pNew->pUp = p;
|
||
+ break;
|
||
+ }
|
||
+ }else if( c>0 ){
|
||
+ if( p->pAfter ){
|
||
+ p = p->pAfter;
|
||
+ }else{
|
||
+ p->pAfter = pNew;
|
||
+ pNew->pUp = p;
|
||
+ break;
|
||
+ }
|
||
+ }else{
|
||
+ return p;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ pNew->pBefore = 0;
|
||
+ pNew->pAfter = 0;
|
||
+ pNew->height = 1;
|
||
+ pNew->imbalance = 0;
|
||
+ *ppHead = amatchAvlBalance(p);
|
||
+ /* assert( amatchAvlIntegrity(*ppHead) ); */
|
||
+ /* assert( amatchAvlIntegrity2(*ppHead) ); */
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Remove node pOld from the tree. pOld must be an element of the tree or
|
||
+** the AVL tree will become corrupt.
|
||
+*/
|
||
+static void amatchAvlRemove(amatch_avl **ppHead, amatch_avl *pOld){
|
||
+ amatch_avl **ppParent;
|
||
+ amatch_avl *pBalance;
|
||
+ /* assert( amatchAvlSearch(*ppHead, pOld->zKey)==pOld ); */
|
||
+ ppParent = amatchAvlFromPtr(pOld, ppHead);
|
||
+ if( pOld->pBefore==0 && pOld->pAfter==0 ){
|
||
+ *ppParent = 0;
|
||
+ pBalance = pOld->pUp;
|
||
+ }else if( pOld->pBefore && pOld->pAfter ){
|
||
+ amatch_avl *pX, *pY;
|
||
+ pX = amatchAvlFirst(pOld->pAfter);
|
||
+ *amatchAvlFromPtr(pX, 0) = pX->pAfter;
|
||
+ if( pX->pAfter ) pX->pAfter->pUp = pX->pUp;
|
||
+ pBalance = pX->pUp;
|
||
+ pX->pAfter = pOld->pAfter;
|
||
+ if( pX->pAfter ){
|
||
+ pX->pAfter->pUp = pX;
|
||
+ }else{
|
||
+ assert( pBalance==pOld );
|
||
+ pBalance = pX;
|
||
+ }
|
||
+ pX->pBefore = pY = pOld->pBefore;
|
||
+ if( pY ) pY->pUp = pX;
|
||
+ pX->pUp = pOld->pUp;
|
||
+ *ppParent = pX;
|
||
+ }else if( pOld->pBefore==0 ){
|
||
+ *ppParent = pBalance = pOld->pAfter;
|
||
+ pBalance->pUp = pOld->pUp;
|
||
+ }else if( pOld->pAfter==0 ){
|
||
+ *ppParent = pBalance = pOld->pBefore;
|
||
+ pBalance->pUp = pOld->pUp;
|
||
+ }
|
||
+ *ppHead = amatchAvlBalance(pBalance);
|
||
+ pOld->pUp = 0;
|
||
+ pOld->pBefore = 0;
|
||
+ pOld->pAfter = 0;
|
||
+ /* assert( amatchAvlIntegrity(*ppHead) ); */
|
||
+ /* assert( amatchAvlIntegrity2(*ppHead) ); */
|
||
+}
|
||
+/*
|
||
+** End of the AVL Tree implementation
|
||
+******************************************************************************/
|
||
+
|
||
+
|
||
+/*
|
||
+** Various types.
|
||
+**
|
||
+** amatch_cost is the "cost" of an edit operation.
|
||
+**
|
||
+** amatch_len is the length of a matching string.
|
||
+**
|
||
+** amatch_langid is an ruleset identifier.
|
||
+*/
|
||
+typedef int amatch_cost;
|
||
+typedef signed char amatch_len;
|
||
+typedef int amatch_langid;
|
||
+
|
||
+/*
|
||
+** Limits
|
||
+*/
|
||
+#define AMATCH_MX_LENGTH 50 /* Maximum length of a rule string */
|
||
+#define AMATCH_MX_LANGID 2147483647 /* Maximum rule ID */
|
||
+#define AMATCH_MX_COST 1000 /* Maximum single-rule cost */
|
||
+
|
||
+/*
|
||
+** A match or partial match
|
||
+*/
|
||
+struct amatch_word {
|
||
+ amatch_word *pNext; /* Next on a list of all amatch_words */
|
||
+ amatch_avl sCost; /* Linkage of this node into the cost tree */
|
||
+ amatch_avl sWord; /* Linkage of this node into the word tree */
|
||
+ amatch_cost rCost; /* Cost of the match so far */
|
||
+ int iSeq; /* Sequence number */
|
||
+ char zCost[10]; /* Cost key (text rendering of rCost) */
|
||
+ short int nMatch; /* Input characters matched */
|
||
+ char zWord[4]; /* Text of the word. Extra space appended as needed */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Each transformation rule is stored as an instance of this object.
|
||
+** All rules are kept on a linked list sorted by rCost.
|
||
+*/
|
||
+struct amatch_rule {
|
||
+ amatch_rule *pNext; /* Next rule in order of increasing rCost */
|
||
+ char *zFrom; /* Transform from (a string from user input) */
|
||
+ amatch_cost rCost; /* Cost of this transformation */
|
||
+ amatch_langid iLang; /* The langauge to which this rule belongs */
|
||
+ amatch_len nFrom, nTo; /* Length of the zFrom and zTo strings */
|
||
+ char zTo[4]; /* Tranform to V.W value (extra space appended) */
|
||
+};
|
||
+
|
||
+/*
|
||
+** A amatch virtual-table object
|
||
+*/
|
||
+struct amatch_vtab {
|
||
+ sqlite3_vtab base; /* Base class - must be first */
|
||
+ char *zClassName; /* Name of this class. Default: "amatch" */
|
||
+ char *zDb; /* Name of database. (ex: "main") */
|
||
+ char *zSelf; /* Name of this virtual table */
|
||
+ char *zCostTab; /* Name of edit-cost-table */
|
||
+ char *zVocabTab; /* Name of vocabulary table */
|
||
+ char *zVocabWord; /* Name of vocabulary table word column */
|
||
+ char *zVocabLang; /* Name of vocabulary table language column */
|
||
+ amatch_rule *pRule; /* All active rules in this amatch */
|
||
+ amatch_cost rIns; /* Generic insertion cost '' -> ? */
|
||
+ amatch_cost rDel; /* Generic deletion cost ? -> '' */
|
||
+ amatch_cost rSub; /* Generic substitution cost ? -> ? */
|
||
+ sqlite3 *db; /* The database connection */
|
||
+ sqlite3_stmt *pVCheck; /* Query to check zVocabTab */
|
||
+ int nCursor; /* Number of active cursors */
|
||
+};
|
||
+
|
||
+/* A amatch cursor object */
|
||
+struct amatch_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ sqlite3_int64 iRowid; /* The rowid of the current word */
|
||
+ amatch_langid iLang; /* Use this language ID */
|
||
+ amatch_cost rLimit; /* Maximum cost of any term */
|
||
+ int nBuf; /* Space allocated for zBuf */
|
||
+ int oomErr; /* True following an OOM error */
|
||
+ int nWord; /* Number of amatch_word objects */
|
||
+ char *zBuf; /* Temp-use buffer space */
|
||
+ char *zInput; /* Input word to match against */
|
||
+ amatch_vtab *pVtab; /* The virtual table this cursor belongs to */
|
||
+ amatch_word *pAllWords; /* List of all amatch_word objects */
|
||
+ amatch_word *pCurrent; /* Most recent solution */
|
||
+ amatch_avl *pCost; /* amatch_word objects keyed by iCost */
|
||
+ amatch_avl *pWord; /* amatch_word objects keyed by zWord */
|
||
+};
|
||
+
|
||
+/*
|
||
+** The two input rule lists are both sorted in order of increasing
|
||
+** cost. Merge them together into a single list, sorted by cost, and
|
||
+** return a pointer to the head of that list.
|
||
+*/
|
||
+static amatch_rule *amatchMergeRules(amatch_rule *pA, amatch_rule *pB){
|
||
+ amatch_rule head;
|
||
+ amatch_rule *pTail;
|
||
+
|
||
+ pTail = &head;
|
||
+ while( pA && pB ){
|
||
+ if( pA->rCost<=pB->rCost ){
|
||
+ pTail->pNext = pA;
|
||
+ pTail = pA;
|
||
+ pA = pA->pNext;
|
||
+ }else{
|
||
+ pTail->pNext = pB;
|
||
+ pTail = pB;
|
||
+ pB = pB->pNext;
|
||
+ }
|
||
+ }
|
||
+ if( pA==0 ){
|
||
+ pTail->pNext = pB;
|
||
+ }else{
|
||
+ pTail->pNext = pA;
|
||
+ }
|
||
+ return head.pNext;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Statement pStmt currently points to a row in the amatch data table. This
|
||
+** function allocates and populates a amatch_rule structure according to
|
||
+** the content of the row.
|
||
+**
|
||
+** If successful, *ppRule is set to point to the new object and SQLITE_OK
|
||
+** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
|
||
+** to an error message and an SQLite error code returned.
|
||
+*/
|
||
+static int amatchLoadOneRule(
|
||
+ amatch_vtab *p, /* Fuzzer virtual table handle */
|
||
+ sqlite3_stmt *pStmt, /* Base rule on statements current row */
|
||
+ amatch_rule **ppRule, /* OUT: New rule object */
|
||
+ char **pzErr /* OUT: Error message */
|
||
+){
|
||
+ sqlite3_int64 iLang = sqlite3_column_int64(pStmt, 0);
|
||
+ const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
|
||
+ const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
|
||
+ amatch_cost rCost = sqlite3_column_int(pStmt, 3);
|
||
+
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ int nFrom; /* Size of string zFrom, in bytes */
|
||
+ int nTo; /* Size of string zTo, in bytes */
|
||
+ amatch_rule *pRule = 0; /* New rule object to return */
|
||
+
|
||
+ if( zFrom==0 ) zFrom = "";
|
||
+ if( zTo==0 ) zTo = "";
|
||
+ nFrom = (int)strlen(zFrom);
|
||
+ nTo = (int)strlen(zTo);
|
||
+
|
||
+ /* Silently ignore null transformations */
|
||
+ if( strcmp(zFrom, zTo)==0 ){
|
||
+ if( zFrom[0]=='?' && zFrom[1]==0 ){
|
||
+ if( p->rSub==0 || p->rSub>rCost ) p->rSub = rCost;
|
||
+ }
|
||
+ *ppRule = 0;
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+
|
||
+ if( rCost<=0 || rCost>AMATCH_MX_COST ){
|
||
+ *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
|
||
+ p->zClassName, AMATCH_MX_COST
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else
|
||
+ if( nFrom>AMATCH_MX_LENGTH || nTo>AMATCH_MX_LENGTH ){
|
||
+ *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
|
||
+ p->zClassName, AMATCH_MX_LENGTH
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else
|
||
+ if( iLang<0 || iLang>AMATCH_MX_LANGID ){
|
||
+ *pzErr = sqlite3_mprintf("%s: iLang must be between 0 and %d",
|
||
+ p->zClassName, AMATCH_MX_LANGID
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else
|
||
+ if( strcmp(zFrom,"")==0 && strcmp(zTo,"?")==0 ){
|
||
+ if( p->rIns==0 || p->rIns>rCost ) p->rIns = rCost;
|
||
+ }else
|
||
+ if( strcmp(zFrom,"?")==0 && strcmp(zTo,"")==0 ){
|
||
+ if( p->rDel==0 || p->rDel>rCost ) p->rDel = rCost;
|
||
+ }else
|
||
+ {
|
||
+ pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
|
||
+ if( pRule==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ memset(pRule, 0, sizeof(*pRule));
|
||
+ pRule->zFrom = &pRule->zTo[nTo+1];
|
||
+ pRule->nFrom = nFrom;
|
||
+ memcpy(pRule->zFrom, zFrom, nFrom+1);
|
||
+ memcpy(pRule->zTo, zTo, nTo+1);
|
||
+ pRule->nTo = nTo;
|
||
+ pRule->rCost = rCost;
|
||
+ pRule->iLang = (int)iLang;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ *ppRule = pRule;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Free all the content in the edit-cost-table
|
||
+*/
|
||
+static void amatchFreeRules(amatch_vtab *p){
|
||
+ while( p->pRule ){
|
||
+ amatch_rule *pRule = p->pRule;
|
||
+ p->pRule = pRule->pNext;
|
||
+ sqlite3_free(pRule);
|
||
+ }
|
||
+ p->pRule = 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Load the content of the amatch data table into memory.
|
||
+*/
|
||
+static int amatchLoadRules(
|
||
+ sqlite3 *db, /* Database handle */
|
||
+ amatch_vtab *p, /* Virtual amatch table to configure */
|
||
+ char **pzErr /* OUT: Error message */
|
||
+){
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ char *zSql; /* SELECT used to read from rules table */
|
||
+ amatch_rule *pHead = 0;
|
||
+
|
||
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zCostTab);
|
||
+ if( zSql==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ int rc2; /* finalize() return code */
|
||
+ sqlite3_stmt *pStmt = 0;
|
||
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
|
||
+ }else if( sqlite3_column_count(pStmt)!=4 ){
|
||
+ *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
|
||
+ p->zClassName, p->zCostTab, sqlite3_column_count(pStmt)
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else{
|
||
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||
+ amatch_rule *pRule = 0;
|
||
+ rc = amatchLoadOneRule(p, pStmt, &pRule, pzErr);
|
||
+ if( pRule ){
|
||
+ pRule->pNext = pHead;
|
||
+ pHead = pRule;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ rc2 = sqlite3_finalize(pStmt);
|
||
+ if( rc==SQLITE_OK ) rc = rc2;
|
||
+ }
|
||
+ sqlite3_free(zSql);
|
||
+
|
||
+ /* All rules are now in a singly linked list starting at pHead. This
|
||
+ ** block sorts them by cost and then sets amatch_vtab.pRule to point to
|
||
+ ** point to the head of the sorted list.
|
||
+ */
|
||
+ if( rc==SQLITE_OK ){
|
||
+ unsigned int i;
|
||
+ amatch_rule *pX;
|
||
+ amatch_rule *a[15];
|
||
+ for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
|
||
+ while( (pX = pHead)!=0 ){
|
||
+ pHead = pX->pNext;
|
||
+ pX->pNext = 0;
|
||
+ for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
|
||
+ pX = amatchMergeRules(a[i], pX);
|
||
+ a[i] = 0;
|
||
+ }
|
||
+ a[i] = amatchMergeRules(a[i], pX);
|
||
+ }
|
||
+ for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
|
||
+ pX = amatchMergeRules(a[i], pX);
|
||
+ }
|
||
+ p->pRule = amatchMergeRules(p->pRule, pX);
|
||
+ }else{
|
||
+ /* An error has occurred. Setting p->pRule to point to the head of the
|
||
+ ** allocated list ensures that the list will be cleaned up in this case.
|
||
+ */
|
||
+ assert( p->pRule==0 );
|
||
+ p->pRule = pHead;
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This function converts an SQL quoted string into an unquoted string
|
||
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
||
+** containing the result. The caller should eventually free this buffer
|
||
+** using sqlite3_free.
|
||
+**
|
||
+** Examples:
|
||
+**
|
||
+** "abc" becomes abc
|
||
+** 'xyz' becomes xyz
|
||
+** [pqr] becomes pqr
|
||
+** `mno` becomes mno
|
||
+*/
|
||
+static char *amatchDequote(const char *zIn){
|
||
+ int nIn; /* Size of input string, in bytes */
|
||
+ char *zOut; /* Output (dequoted) string */
|
||
+
|
||
+ nIn = (int)strlen(zIn);
|
||
+ zOut = sqlite3_malloc(nIn+1);
|
||
+ if( zOut ){
|
||
+ char q = zIn[0]; /* Quote character (if any ) */
|
||
+
|
||
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
||
+ memcpy(zOut, zIn, nIn+1);
|
||
+ }else{
|
||
+ int iOut = 0; /* Index of next byte to write to output */
|
||
+ int iIn; /* Index of next byte to read from input */
|
||
+
|
||
+ if( q=='[' ) q = ']';
|
||
+ for(iIn=1; iIn<nIn; iIn++){
|
||
+ if( zIn[iIn]==q ) iIn++;
|
||
+ zOut[iOut++] = zIn[iIn];
|
||
+ }
|
||
+ }
|
||
+ assert( (int)strlen(zOut)<=nIn );
|
||
+ }
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Deallocate the pVCheck prepared statement.
|
||
+*/
|
||
+static void amatchVCheckClear(amatch_vtab *p){
|
||
+ if( p->pVCheck ){
|
||
+ sqlite3_finalize(p->pVCheck);
|
||
+ p->pVCheck = 0;
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Deallocate an amatch_vtab object
|
||
+*/
|
||
+static void amatchFree(amatch_vtab *p){
|
||
+ if( p ){
|
||
+ amatchFreeRules(p);
|
||
+ amatchVCheckClear(p);
|
||
+ sqlite3_free(p->zClassName);
|
||
+ sqlite3_free(p->zDb);
|
||
+ sqlite3_free(p->zCostTab);
|
||
+ sqlite3_free(p->zVocabTab);
|
||
+ sqlite3_free(p->zVocabWord);
|
||
+ sqlite3_free(p->zVocabLang);
|
||
+ sqlite3_free(p->zSelf);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+ sqlite3_free(p);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** xDisconnect/xDestroy method for the amatch module.
|
||
+*/
|
||
+static int amatchDisconnect(sqlite3_vtab *pVtab){
|
||
+ amatch_vtab *p = (amatch_vtab*)pVtab;
|
||
+ assert( p->nCursor==0 );
|
||
+ amatchFree(p);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Check to see if the argument is of the form:
|
||
+**
|
||
+** KEY = VALUE
|
||
+**
|
||
+** If it is, return a pointer to the first character of VALUE.
|
||
+** If not, return NULL. Spaces around the = are ignored.
|
||
+*/
|
||
+static const char *amatchValueOfKey(const char *zKey, const char *zStr){
|
||
+ int nKey = (int)strlen(zKey);
|
||
+ int nStr = (int)strlen(zStr);
|
||
+ int i;
|
||
+ if( nStr<nKey+1 ) return 0;
|
||
+ if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
||
+ for(i=nKey; isspace(zStr[i]); i++){}
|
||
+ if( zStr[i]!='=' ) return 0;
|
||
+ i++;
|
||
+ while( isspace(zStr[i]) ){ i++; }
|
||
+ return zStr+i;
|
||
+}
|
||
+
|
||
+/*
|
||
+** xConnect/xCreate method for the amatch module. Arguments are:
|
||
+**
|
||
+** argv[0] -> module name ("approximate_match")
|
||
+** argv[1] -> database name
|
||
+** argv[2] -> table name
|
||
+** argv[3...] -> arguments
|
||
+*/
|
||
+static int amatchConnect(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ amatch_vtab *pNew = 0; /* New virtual table */
|
||
+ const char *zModule = argv[0];
|
||
+ const char *zDb = argv[1];
|
||
+ const char *zVal;
|
||
+ int i;
|
||
+
|
||
+ (void)pAux;
|
||
+ *ppVtab = 0;
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ rc = SQLITE_NOMEM;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->db = db;
|
||
+ pNew->zClassName = sqlite3_mprintf("%s", zModule);
|
||
+ if( pNew->zClassName==0 ) goto amatchConnectError;
|
||
+ pNew->zDb = sqlite3_mprintf("%s", zDb);
|
||
+ if( pNew->zDb==0 ) goto amatchConnectError;
|
||
+ pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
||
+ if( pNew->zSelf==0 ) goto amatchConnectError;
|
||
+ for(i=3; i<argc; i++){
|
||
+ zVal = amatchValueOfKey("vocabulary_table", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zVocabTab);
|
||
+ pNew->zVocabTab = amatchDequote(zVal);
|
||
+ if( pNew->zVocabTab==0 ) goto amatchConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ zVal = amatchValueOfKey("vocabulary_word", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zVocabWord);
|
||
+ pNew->zVocabWord = amatchDequote(zVal);
|
||
+ if( pNew->zVocabWord==0 ) goto amatchConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ zVal = amatchValueOfKey("vocabulary_language", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zVocabLang);
|
||
+ pNew->zVocabLang = amatchDequote(zVal);
|
||
+ if( pNew->zVocabLang==0 ) goto amatchConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ zVal = amatchValueOfKey("edit_distances", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zCostTab);
|
||
+ pNew->zCostTab = amatchDequote(zVal);
|
||
+ if( pNew->zCostTab==0 ) goto amatchConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
||
+ amatchFree(pNew);
|
||
+ *ppVtab = 0;
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ rc = SQLITE_OK;
|
||
+ if( pNew->zCostTab==0 ){
|
||
+ *pzErr = sqlite3_mprintf("no edit_distances table specified");
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else{
|
||
+ rc = amatchLoadRules(db, pNew, pzErr);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_declare_vtab(db,
|
||
+ "CREATE TABLE x(word,distance,language,"
|
||
+ "command HIDDEN,nword HIDDEN)"
|
||
+ );
|
||
+#define AMATCH_COL_WORD 0
|
||
+#define AMATCH_COL_DISTANCE 1
|
||
+#define AMATCH_COL_LANGUAGE 2
|
||
+#define AMATCH_COL_COMMAND 3
|
||
+#define AMATCH_COL_NWORD 4
|
||
+ }
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ amatchFree(pNew);
|
||
+ }
|
||
+ *ppVtab = &pNew->base;
|
||
+ return rc;
|
||
+
|
||
+amatchConnectError:
|
||
+ amatchFree(pNew);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open a new amatch cursor.
|
||
+*/
|
||
+static int amatchOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||
+ amatch_vtab *p = (amatch_vtab*)pVTab;
|
||
+ amatch_cursor *pCur;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ pCur->pVtab = p;
|
||
+ *ppCursor = &pCur->base;
|
||
+ p->nCursor++;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
||
+** to indicate that it is at EOF.
|
||
+*/
|
||
+static void amatchClearCursor(amatch_cursor *pCur){
|
||
+ amatch_word *pWord, *pNextWord;
|
||
+ for(pWord=pCur->pAllWords; pWord; pWord=pNextWord){
|
||
+ pNextWord = pWord->pNext;
|
||
+ sqlite3_free(pWord);
|
||
+ }
|
||
+ pCur->pAllWords = 0;
|
||
+ sqlite3_free(pCur->zInput);
|
||
+ pCur->zInput = 0;
|
||
+ sqlite3_free(pCur->zBuf);
|
||
+ pCur->zBuf = 0;
|
||
+ pCur->nBuf = 0;
|
||
+ pCur->pCost = 0;
|
||
+ pCur->pWord = 0;
|
||
+ pCur->pCurrent = 0;
|
||
+ pCur->rLimit = 1000000;
|
||
+ pCur->iLang = 0;
|
||
+ pCur->nWord = 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Close a amatch cursor.
|
||
+*/
|
||
+static int amatchClose(sqlite3_vtab_cursor *cur){
|
||
+ amatch_cursor *pCur = (amatch_cursor *)cur;
|
||
+ amatchClearCursor(pCur);
|
||
+ pCur->pVtab->nCursor--;
|
||
+ sqlite3_free(pCur);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Render a 24-bit unsigned integer as a 4-byte base-64 number.
|
||
+*/
|
||
+static void amatchEncodeInt(int x, char *z){
|
||
+ static const char a[] =
|
||
+ "0123456789"
|
||
+ "ABCDEFGHIJ"
|
||
+ "KLMNOPQRST"
|
||
+ "UVWXYZ^abc"
|
||
+ "defghijklm"
|
||
+ "nopqrstuvw"
|
||
+ "xyz~";
|
||
+ z[0] = a[(x>>18)&0x3f];
|
||
+ z[1] = a[(x>>12)&0x3f];
|
||
+ z[2] = a[(x>>6)&0x3f];
|
||
+ z[3] = a[x&0x3f];
|
||
+}
|
||
+
|
||
+/*
|
||
+** Write the zCost[] field for a amatch_word object
|
||
+*/
|
||
+static void amatchWriteCost(amatch_word *pWord){
|
||
+ amatchEncodeInt(pWord->rCost, pWord->zCost);
|
||
+ amatchEncodeInt(pWord->iSeq, pWord->zCost+4);
|
||
+ pWord->zCost[8] = 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Add a new amatch_word object to the queue.
|
||
+**
|
||
+** If a prior amatch_word object with the same zWord, and nMatch
|
||
+** already exists, update its rCost (if the new rCost is less) but
|
||
+** otherwise leave it unchanged. Do not add a duplicate.
|
||
+**
|
||
+** Do nothing if the cost exceeds threshold.
|
||
+*/
|
||
+static void amatchAddWord(
|
||
+ amatch_cursor *pCur,
|
||
+ amatch_cost rCost,
|
||
+ int nMatch,
|
||
+ const char *zWordBase,
|
||
+ const char *zWordTail
|
||
+){
|
||
+ amatch_word *pWord;
|
||
+ amatch_avl *pNode;
|
||
+ amatch_avl *pOther;
|
||
+ int nBase, nTail;
|
||
+ char zBuf[4];
|
||
+
|
||
+ if( rCost>pCur->rLimit ){
|
||
+ return;
|
||
+ }
|
||
+ nBase = (int)strlen(zWordBase);
|
||
+ nTail = (int)strlen(zWordTail);
|
||
+ if( nBase+nTail+3>pCur->nBuf ){
|
||
+ pCur->nBuf = nBase+nTail+100;
|
||
+ pCur->zBuf = sqlite3_realloc(pCur->zBuf, pCur->nBuf);
|
||
+ if( pCur->zBuf==0 ){
|
||
+ pCur->nBuf = 0;
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+ amatchEncodeInt(nMatch, zBuf);
|
||
+ memcpy(pCur->zBuf, zBuf+2, 2);
|
||
+ memcpy(pCur->zBuf+2, zWordBase, nBase);
|
||
+ memcpy(pCur->zBuf+2+nBase, zWordTail, nTail+1);
|
||
+ pNode = amatchAvlSearch(pCur->pWord, pCur->zBuf);
|
||
+ if( pNode ){
|
||
+ pWord = pNode->pWord;
|
||
+ if( pWord->rCost>rCost ){
|
||
+#ifdef AMATCH_TRACE_1
|
||
+ printf("UPDATE [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
|
||
+ pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput,
|
||
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
||
+#endif
|
||
+ amatchAvlRemove(&pCur->pCost, &pWord->sCost);
|
||
+ pWord->rCost = rCost;
|
||
+ amatchWriteCost(pWord);
|
||
+#ifdef AMATCH_TRACE_1
|
||
+ printf(" ---> %d (\"%s\" \"%s\")\n",
|
||
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
||
+#endif
|
||
+ pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
|
||
+ assert( pOther==0 ); (void)pOther;
|
||
+ }
|
||
+ return;
|
||
+ }
|
||
+ pWord = sqlite3_malloc( sizeof(*pWord) + nBase + nTail - 1 );
|
||
+ if( pWord==0 ) return;
|
||
+ memset(pWord, 0, sizeof(*pWord));
|
||
+ pWord->rCost = rCost;
|
||
+ pWord->iSeq = pCur->nWord++;
|
||
+ amatchWriteCost(pWord);
|
||
+ pWord->nMatch = nMatch;
|
||
+ pWord->pNext = pCur->pAllWords;
|
||
+ pCur->pAllWords = pWord;
|
||
+ pWord->sCost.zKey = pWord->zCost;
|
||
+ pWord->sCost.pWord = pWord;
|
||
+ pOther = amatchAvlInsert(&pCur->pCost, &pWord->sCost);
|
||
+ assert( pOther==0 ); (void)pOther;
|
||
+ pWord->sWord.zKey = pWord->zWord;
|
||
+ pWord->sWord.pWord = pWord;
|
||
+ strcpy(pWord->zWord, pCur->zBuf);
|
||
+ pOther = amatchAvlInsert(&pCur->pWord, &pWord->sWord);
|
||
+ assert( pOther==0 ); (void)pOther;
|
||
+#ifdef AMATCH_TRACE_1
|
||
+ printf("INSERT [%s][%.*s^%s] %d (\"%s\" \"%s\")\n", pWord->zWord+2,
|
||
+ pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch, rCost,
|
||
+ pWord->zWord, pWord->zCost);
|
||
+#endif
|
||
+}
|
||
+
|
||
+/*
|
||
+** Advance a cursor to its next row of output
|
||
+*/
|
||
+static int amatchNext(sqlite3_vtab_cursor *cur){
|
||
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
||
+ amatch_word *pWord = 0;
|
||
+ amatch_avl *pNode;
|
||
+ int isMatch = 0;
|
||
+ amatch_vtab *p = pCur->pVtab;
|
||
+ int nWord;
|
||
+ int rc;
|
||
+ int i;
|
||
+ const char *zW;
|
||
+ amatch_rule *pRule;
|
||
+ char *zBuf = 0;
|
||
+ char nBuf = 0;
|
||
+ char zNext[8];
|
||
+ char zNextIn[8];
|
||
+ int nNextIn;
|
||
+
|
||
+ if( p->pVCheck==0 ){
|
||
+ char *zSql;
|
||
+ if( p->zVocabLang && p->zVocabLang[0] ){
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT \"%w\" FROM \"%w\"",
|
||
+ " WHERE \"%w\">=?1 AND \"%w\"=?2"
|
||
+ " ORDER BY 1",
|
||
+ p->zVocabWord, p->zVocabTab,
|
||
+ p->zVocabWord, p->zVocabLang
|
||
+ );
|
||
+ }else{
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT \"%w\" FROM \"%w\""
|
||
+ " WHERE \"%w\">=?1"
|
||
+ " ORDER BY 1",
|
||
+ p->zVocabWord, p->zVocabTab,
|
||
+ p->zVocabWord
|
||
+ );
|
||
+ }
|
||
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->pVCheck, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ if( rc ) return rc;
|
||
+ }
|
||
+ sqlite3_bind_int(p->pVCheck, 2, pCur->iLang);
|
||
+
|
||
+ do{
|
||
+ pNode = amatchAvlFirst(pCur->pCost);
|
||
+ if( pNode==0 ){
|
||
+ pWord = 0;
|
||
+ break;
|
||
+ }
|
||
+ pWord = pNode->pWord;
|
||
+ amatchAvlRemove(&pCur->pCost, &pWord->sCost);
|
||
+
|
||
+#ifdef AMATCH_TRACE_1
|
||
+ printf("PROCESS [%s][%.*s^%s] %d (\"%s\" \"%s\")\n",
|
||
+ pWord->zWord+2, pWord->nMatch, pCur->zInput, pCur->zInput+pWord->nMatch,
|
||
+ pWord->rCost, pWord->zWord, pWord->zCost);
|
||
+#endif
|
||
+ nWord = (int)strlen(pWord->zWord+2);
|
||
+ if( nWord+20>nBuf ){
|
||
+ nBuf = nWord+100;
|
||
+ zBuf = sqlite3_realloc(zBuf, nBuf);
|
||
+ if( zBuf==0 ) return SQLITE_NOMEM;
|
||
+ }
|
||
+ strcpy(zBuf, pWord->zWord+2);
|
||
+ zNext[0] = 0;
|
||
+ zNextIn[0] = pCur->zInput[pWord->nMatch];
|
||
+ if( zNextIn[0] ){
|
||
+ for(i=1; i<=4 && (pCur->zInput[pWord->nMatch+i]&0xc0)==0x80; i++){
|
||
+ zNextIn[i] = pCur->zInput[pWord->nMatch+i];
|
||
+ }
|
||
+ zNextIn[i] = 0;
|
||
+ nNextIn = i;
|
||
+ }else{
|
||
+ nNextIn = 0;
|
||
+ }
|
||
+
|
||
+ if( zNextIn[0] && zNextIn[0]!='*' ){
|
||
+ sqlite3_reset(p->pVCheck);
|
||
+ strcat(zBuf, zNextIn);
|
||
+ sqlite3_bind_text(p->pVCheck, 1, zBuf, nWord+nNextIn, SQLITE_STATIC);
|
||
+ rc = sqlite3_step(p->pVCheck);
|
||
+ if( rc==SQLITE_ROW ){
|
||
+ zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
|
||
+ if( strncmp(zBuf, zW, nWord+nNextIn)==0 ){
|
||
+ amatchAddWord(pCur, pWord->rCost, pWord->nMatch+nNextIn, zBuf, "");
|
||
+ }
|
||
+ }
|
||
+ zBuf[nWord] = 0;
|
||
+ }
|
||
+
|
||
+ while( 1 ){
|
||
+ strcpy(zBuf+nWord, zNext);
|
||
+ sqlite3_reset(p->pVCheck);
|
||
+ sqlite3_bind_text(p->pVCheck, 1, zBuf, -1, SQLITE_TRANSIENT);
|
||
+ rc = sqlite3_step(p->pVCheck);
|
||
+ if( rc!=SQLITE_ROW ) break;
|
||
+ zW = (const char*)sqlite3_column_text(p->pVCheck, 0);
|
||
+ strcpy(zBuf+nWord, zNext);
|
||
+ if( strncmp(zW, zBuf, nWord)!=0 ) break;
|
||
+ if( (zNextIn[0]=='*' && zNextIn[1]==0)
|
||
+ || (zNextIn[0]==0 && zW[nWord]==0)
|
||
+ ){
|
||
+ isMatch = 1;
|
||
+ zNextIn[0] = 0;
|
||
+ nNextIn = 0;
|
||
+ break;
|
||
+ }
|
||
+ zNext[0] = zW[nWord];
|
||
+ for(i=1; i<=4 && (zW[nWord+i]&0xc0)==0x80; i++){
|
||
+ zNext[i] = zW[nWord+i];
|
||
+ }
|
||
+ zNext[i] = 0;
|
||
+ zBuf[nWord] = 0;
|
||
+ if( p->rIns>0 ){
|
||
+ amatchAddWord(pCur, pWord->rCost+p->rIns, pWord->nMatch,
|
||
+ zBuf, zNext);
|
||
+ }
|
||
+ if( p->rSub>0 ){
|
||
+ amatchAddWord(pCur, pWord->rCost+p->rSub, pWord->nMatch+nNextIn,
|
||
+ zBuf, zNext);
|
||
+ }
|
||
+ if( p->rIns<0 && p->rSub<0 ) break;
|
||
+ zNext[i-1]++; /* FIX ME */
|
||
+ }
|
||
+ sqlite3_reset(p->pVCheck);
|
||
+
|
||
+ if( p->rDel>0 ){
|
||
+ zBuf[nWord] = 0;
|
||
+ amatchAddWord(pCur, pWord->rCost+p->rDel, pWord->nMatch+nNextIn,
|
||
+ zBuf, "");
|
||
+ }
|
||
+
|
||
+ for(pRule=p->pRule; pRule; pRule=pRule->pNext){
|
||
+ if( pRule->iLang!=pCur->iLang ) continue;
|
||
+ if( strncmp(pRule->zFrom, pCur->zInput+pWord->nMatch, pRule->nFrom)==0 ){
|
||
+ amatchAddWord(pCur, pWord->rCost+pRule->rCost,
|
||
+ pWord->nMatch+pRule->nFrom, pWord->zWord+2, pRule->zTo);
|
||
+ }
|
||
+ }
|
||
+ }while( !isMatch );
|
||
+ pCur->pCurrent = pWord;
|
||
+ sqlite3_free(zBuf);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Called to "rewind" a cursor back to the beginning so that
|
||
+** it starts its output over again. Always called at least once
|
||
+** prior to any amatchColumn, amatchRowid, or amatchEof call.
|
||
+*/
|
||
+static int amatchFilter(
|
||
+ sqlite3_vtab_cursor *pVtabCursor,
|
||
+ int idxNum, const char *idxStr,
|
||
+ int argc, sqlite3_value **argv
|
||
+){
|
||
+ amatch_cursor *pCur = (amatch_cursor *)pVtabCursor;
|
||
+ const char *zWord = "*";
|
||
+ int idx;
|
||
+
|
||
+ amatchClearCursor(pCur);
|
||
+ idx = 0;
|
||
+ if( idxNum & 1 ){
|
||
+ zWord = (const char*)sqlite3_value_text(argv[0]);
|
||
+ idx++;
|
||
+ }
|
||
+ if( idxNum & 2 ){
|
||
+ pCur->rLimit = (amatch_cost)sqlite3_value_int(argv[idx]);
|
||
+ idx++;
|
||
+ }
|
||
+ if( idxNum & 4 ){
|
||
+ pCur->iLang = (amatch_cost)sqlite3_value_int(argv[idx]);
|
||
+ idx++;
|
||
+ }
|
||
+ pCur->zInput = sqlite3_mprintf("%s", zWord);
|
||
+ if( pCur->zInput==0 ) return SQLITE_NOMEM;
|
||
+ amatchAddWord(pCur, 0, 0, "", "");
|
||
+ amatchNext(pVtabCursor);
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Only the word and distance columns have values. All other columns
|
||
+** return NULL
|
||
+*/
|
||
+static int amatchColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
||
+ switch( i ){
|
||
+ case AMATCH_COL_WORD: {
|
||
+ sqlite3_result_text(ctx, pCur->pCurrent->zWord+2, -1, SQLITE_STATIC);
|
||
+ break;
|
||
+ }
|
||
+ case AMATCH_COL_DISTANCE: {
|
||
+ sqlite3_result_int(ctx, pCur->pCurrent->rCost);
|
||
+ break;
|
||
+ }
|
||
+ case AMATCH_COL_LANGUAGE: {
|
||
+ sqlite3_result_int(ctx, pCur->iLang);
|
||
+ break;
|
||
+ }
|
||
+ case AMATCH_COL_NWORD: {
|
||
+ sqlite3_result_int(ctx, pCur->nWord);
|
||
+ break;
|
||
+ }
|
||
+ default: {
|
||
+ sqlite3_result_null(ctx);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The rowid.
|
||
+*/
|
||
+static int amatchRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
||
+ *pRowid = pCur->iRowid;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** EOF indicator
|
||
+*/
|
||
+static int amatchEof(sqlite3_vtab_cursor *cur){
|
||
+ amatch_cursor *pCur = (amatch_cursor*)cur;
|
||
+ return pCur->pCurrent==0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Search for terms of these forms:
|
||
+**
|
||
+** (A) word MATCH $str
|
||
+** (B1) distance < $value
|
||
+** (B2) distance <= $value
|
||
+** (C) language == $language
|
||
+**
|
||
+** The distance< and distance<= are both treated as distance<=.
|
||
+** The query plan number is a bit vector:
|
||
+**
|
||
+** bit 1: Term of the form (A) found
|
||
+** bit 2: Term like (B1) or (B2) found
|
||
+** bit 3: Term like (C) found
|
||
+**
|
||
+** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
|
||
+** then $value is in filter.argv[0] if bit-1 is clear and is in
|
||
+** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
|
||
+** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
|
||
+** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
|
||
+** filter.argv[2] if both bit-1 and bit-2 are set.
|
||
+*/
|
||
+static int amatchBestIndex(
|
||
+ sqlite3_vtab *tab,
|
||
+ sqlite3_index_info *pIdxInfo
|
||
+){
|
||
+ int iPlan = 0;
|
||
+ int iDistTerm = -1;
|
||
+ int iLangTerm = -1;
|
||
+ int i;
|
||
+ const struct sqlite3_index_constraint *pConstraint;
|
||
+
|
||
+ (void)tab;
|
||
+ pConstraint = pIdxInfo->aConstraint;
|
||
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||
+ if( pConstraint->usable==0 ) continue;
|
||
+ if( (iPlan & 1)==0
|
||
+ && pConstraint->iColumn==0
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
||
+ ){
|
||
+ iPlan |= 1;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ }
|
||
+ if( (iPlan & 2)==0
|
||
+ && pConstraint->iColumn==1
|
||
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
||
+ ){
|
||
+ iPlan |= 2;
|
||
+ iDistTerm = i;
|
||
+ }
|
||
+ if( (iPlan & 4)==0
|
||
+ && pConstraint->iColumn==2
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 4;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ iLangTerm = i;
|
||
+ }
|
||
+ }
|
||
+ if( iPlan & 2 ){
|
||
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
|
||
+ }
|
||
+ if( iPlan & 4 ){
|
||
+ int idx = 1;
|
||
+ if( iPlan & 1 ) idx++;
|
||
+ if( iPlan & 2 ) idx++;
|
||
+ pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx;
|
||
+ }
|
||
+ pIdxInfo->idxNum = iPlan;
|
||
+ if( pIdxInfo->nOrderBy==1
|
||
+ && pIdxInfo->aOrderBy[0].iColumn==1
|
||
+ && pIdxInfo->aOrderBy[0].desc==0
|
||
+ ){
|
||
+ pIdxInfo->orderByConsumed = 1;
|
||
+ }
|
||
+ pIdxInfo->estimatedCost = (double)10000;
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The xUpdate() method.
|
||
+**
|
||
+** This implementation disallows DELETE and UPDATE. The only thing
|
||
+** allowed is INSERT into the "command" column.
|
||
+*/
|
||
+static int amatchUpdate(
|
||
+ sqlite3_vtab *pVTab,
|
||
+ int argc,
|
||
+ sqlite3_value **argv,
|
||
+ sqlite_int64 *pRowid
|
||
+){
|
||
+ amatch_vtab *p = (amatch_vtab*)pVTab;
|
||
+ const unsigned char *zCmd;
|
||
+ (void)pRowid;
|
||
+ if( argc==1 ){
|
||
+ pVTab->zErrMsg = sqlite3_mprintf("DELETE from %s is not allowed",
|
||
+ p->zSelf);
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
|
||
+ pVTab->zErrMsg = sqlite3_mprintf("UPDATE of %s is not allowed",
|
||
+ p->zSelf);
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ if( sqlite3_value_type(argv[2+AMATCH_COL_WORD])!=SQLITE_NULL
|
||
+ || sqlite3_value_type(argv[2+AMATCH_COL_DISTANCE])!=SQLITE_NULL
|
||
+ || sqlite3_value_type(argv[2+AMATCH_COL_LANGUAGE])!=SQLITE_NULL
|
||
+ ){
|
||
+ pVTab->zErrMsg = sqlite3_mprintf(
|
||
+ "INSERT INTO %s allowed for column [command] only", p->zSelf);
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ zCmd = sqlite3_value_text(argv[2+AMATCH_COL_COMMAND]);
|
||
+ if( zCmd==0 ) return SQLITE_OK;
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** A virtual table module that implements the "approximate_match".
|
||
+*/
|
||
+static sqlite3_module amatchModule = {
|
||
+ 0, /* iVersion */
|
||
+ amatchConnect, /* xCreate */
|
||
+ amatchConnect, /* xConnect */
|
||
+ amatchBestIndex, /* xBestIndex */
|
||
+ amatchDisconnect, /* xDisconnect */
|
||
+ amatchDisconnect, /* xDestroy */
|
||
+ amatchOpen, /* xOpen - open a cursor */
|
||
+ amatchClose, /* xClose - close a cursor */
|
||
+ amatchFilter, /* xFilter - configure scan constraints */
|
||
+ amatchNext, /* xNext - advance a cursor */
|
||
+ amatchEof, /* xEof - check for end of scan */
|
||
+ amatchColumn, /* xColumn - read data */
|
||
+ amatchRowid, /* xRowid - read data */
|
||
+ amatchUpdate, /* xUpdate */
|
||
+ 0, /* xBegin */
|
||
+ 0, /* xSync */
|
||
+ 0, /* xCommit */
|
||
+ 0, /* xRollback */
|
||
+ 0, /* xFindMethod */
|
||
+ 0, /* xRename */
|
||
+ 0, /* xSavepoint */
|
||
+ 0, /* xRelease */
|
||
+ 0 /* xRollbackTo */
|
||
+};
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+/*
|
||
+** Register the amatch virtual table
|
||
+*/
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_amatch_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Not used */
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0);
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Not used */
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "approximate_match", &amatchModule, 0);
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/closure.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/closure.c 2015-01-31 00:31:56.353140700 +0100
|
||
@@ -0,0 +1,973 @@
|
||
+/*
|
||
+** 2013-04-16
|
||
+**
|
||
+** 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 contains code for a virtual table that finds the transitive
|
||
+** closure of a parent/child relationship in a real table. The virtual
|
||
+** table is called "transitive_closure".
|
||
+**
|
||
+** A transitive_closure virtual table is created like this:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE x USING transitive_closure(
|
||
+** tablename=<tablename>, -- T
|
||
+** idcolumn=<columnname>, -- X
|
||
+** parentcolumn=<columnname> -- P
|
||
+** );
|
||
+**
|
||
+** When it is created, the new transitive_closure table may be supplied
|
||
+** with default values for the name of a table T and columns T.X and T.P.
|
||
+** The T.X and T.P columns must contain integers. The ideal case is for
|
||
+** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
|
||
+** the T.X column. The row referenced by T.P is the parent of the current row.
|
||
+**
|
||
+** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
|
||
+** TABLE statement may be overridden in individual queries by including
|
||
+** terms like tablename='newtable', idcolumn='id2', or
|
||
+** parentcolumn='parent3' in the WHERE clause of the query.
|
||
+**
|
||
+** For efficiency, it is essential that there be an index on the P column:
|
||
+**
|
||
+** CREATE Tidx1 ON T(P)
|
||
+**
|
||
+** Suppose a specific instance of the closure table is as follows:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
|
||
+** tablename='group',
|
||
+** idcolumn='groupId',
|
||
+** parentcolumn='parentId'
|
||
+** );
|
||
+**
|
||
+** Such an instance of the transitive_closure virtual table would be
|
||
+** appropriate for walking a tree defined using a table like this, for example:
|
||
+**
|
||
+** CREATE TABLE group(
|
||
+** groupId INTEGER PRIMARY KEY,
|
||
+** parentId INTEGER REFERENCES group
|
||
+** );
|
||
+** CREATE INDEX group_idx1 ON group(parentId);
|
||
+**
|
||
+** The group table above would presumably have other application-specific
|
||
+** fields. The key point here is that rows of the group table form a
|
||
+** tree. The purpose of the ct1 virtual table is to easily extract
|
||
+** branches of that tree.
|
||
+**
|
||
+** Once it has been created, the ct1 virtual table can be queried
|
||
+** as follows:
|
||
+**
|
||
+** SELECT * FROM element
|
||
+** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
|
||
+**
|
||
+** The above query will return all elements that are part of group ?1
|
||
+** or children of group ?1 or grand-children of ?1 and so forth for all
|
||
+** descendents of group ?1. The same query can be formulated as a join:
|
||
+**
|
||
+** SELECT element.* FROM element, ct1
|
||
+** WHERE element.groupid=ct1.id
|
||
+** AND ct1.root=?1;
|
||
+**
|
||
+** The depth of the transitive_closure (the number of generations of
|
||
+** parent/child relations to follow) can be limited by setting "depth"
|
||
+** column in the WHERE clause. So, for example, the following query
|
||
+** finds only children and grandchildren but no further descendents:
|
||
+**
|
||
+** SELECT element.* FROM element, ct1
|
||
+** WHERE element.groupid=ct1.id
|
||
+** AND ct1.root=?1
|
||
+** AND ct1.depth<=2;
|
||
+**
|
||
+** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
|
||
+** order to find only the grandchildren of ?1, not ?1 itself or the
|
||
+** children of ?1.
|
||
+**
|
||
+** The root=?1 term must be supplied in WHERE clause or else the query
|
||
+** of the ct1 virtual table will return an empty set. The tablename,
|
||
+** idcolumn, and parentcolumn attributes can be overridden in the WHERE
|
||
+** clause if desired. So, for example, the ct1 table could be repurposed
|
||
+** to find ancestors rather than descendents by inverting the roles of
|
||
+** the idcolumn and parentcolumn:
|
||
+**
|
||
+** SELECT element.* FROM element, ct1
|
||
+** WHERE element.groupid=ct1.id
|
||
+** AND ct1.root=?1
|
||
+** AND ct1.idcolumn='parentId'
|
||
+** AND ct1.parentcolumn='groupId';
|
||
+**
|
||
+** Multiple calls to ct1 could be combined. For example, the following
|
||
+** query finds all elements that "cousins" of groupId ?1. That is to say
|
||
+** elements where the groupId is a grandchild of the grandparent of ?1.
|
||
+** (This definition of "cousins" also includes siblings and self.)
|
||
+**
|
||
+** SELECT element.* FROM element, ct1
|
||
+** WHERE element.groupId=ct1.id
|
||
+** AND ct1.depth=2
|
||
+** AND ct1.root IN (SELECT id FROM ct1
|
||
+** WHERE root=?1
|
||
+** AND depth=2
|
||
+** AND idcolumn='parentId'
|
||
+** AND parentcolumn='groupId');
|
||
+**
|
||
+** In our example, the group.groupId column is unique and thus the
|
||
+** subquery will return exactly one row. For that reason, the IN
|
||
+** operator could be replaced by "=" to get the same result. But
|
||
+** in the general case where the idcolumn is not unique, an IN operator
|
||
+** would be required for this kind of query.
|
||
+**
|
||
+** Note that because the tablename, idcolumn, and parentcolumn can
|
||
+** all be specified in the query, it is possible for an application
|
||
+** to define a single transitive_closure virtual table for use on lots
|
||
+** of different hierarchy tables. One might say:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
|
||
+**
|
||
+** As each database connection is being opened. Then the application
|
||
+** would always have a "closure" virtual table handy to use for querying.
|
||
+**
|
||
+** SELECT element.* FROM element, closure
|
||
+** WHERE element.groupid=ct1.id
|
||
+** AND closure.root=?1
|
||
+** AND closure.tablename='group'
|
||
+** AND closure.idname='groupId'
|
||
+** AND closure.parentname='parentId';
|
||
+**
|
||
+** See the documentation at http://www.sqlite.org/loadext.html for information
|
||
+** on how to compile and use loadable extensions such as this one.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <assert.h>
|
||
+#include <stdio.h>
|
||
+#include <ctype.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+/*
|
||
+** Forward declaration of objects used by this implementation
|
||
+*/
|
||
+typedef struct closure_vtab closure_vtab;
|
||
+typedef struct closure_cursor closure_cursor;
|
||
+typedef struct closure_queue closure_queue;
|
||
+typedef struct closure_avl closure_avl;
|
||
+
|
||
+/*****************************************************************************
|
||
+** AVL Tree implementation
|
||
+*/
|
||
+/*
|
||
+** Objects that want to be members of the AVL tree should embedded an
|
||
+** instance of this structure.
|
||
+*/
|
||
+struct closure_avl {
|
||
+ sqlite3_int64 id; /* Id of this entry in the table */
|
||
+ int iGeneration; /* Which generation is this entry part of */
|
||
+ closure_avl *pList; /* A linked list of nodes */
|
||
+ closure_avl *pBefore; /* Other elements less than id */
|
||
+ closure_avl *pAfter; /* Other elements greater than id */
|
||
+ closure_avl *pUp; /* Parent element */
|
||
+ short int height; /* Height of this node. Leaf==1 */
|
||
+ short int imbalance; /* Height difference between pBefore and pAfter */
|
||
+};
|
||
+
|
||
+/* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
|
||
+** Assume that the children of p have correct heights.
|
||
+*/
|
||
+static void closureAvlRecomputeHeight(closure_avl *p){
|
||
+ short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
||
+ short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
||
+ p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
||
+ p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** P B
|
||
+** / \ / \
|
||
+** B Z ==> X P
|
||
+** / \ / \
|
||
+** X Y Y Z
|
||
+**
|
||
+*/
|
||
+static closure_avl *closureAvlRotateBefore(closure_avl *pP){
|
||
+ closure_avl *pB = pP->pBefore;
|
||
+ closure_avl *pY = pB->pAfter;
|
||
+ pB->pUp = pP->pUp;
|
||
+ pB->pAfter = pP;
|
||
+ pP->pUp = pB;
|
||
+ pP->pBefore = pY;
|
||
+ if( pY ) pY->pUp = pP;
|
||
+ closureAvlRecomputeHeight(pP);
|
||
+ closureAvlRecomputeHeight(pB);
|
||
+ return pB;
|
||
+}
|
||
+
|
||
+/*
|
||
+** P A
|
||
+** / \ / \
|
||
+** X A ==> P Z
|
||
+** / \ / \
|
||
+** Y Z X Y
|
||
+**
|
||
+*/
|
||
+static closure_avl *closureAvlRotateAfter(closure_avl *pP){
|
||
+ closure_avl *pA = pP->pAfter;
|
||
+ closure_avl *pY = pA->pBefore;
|
||
+ pA->pUp = pP->pUp;
|
||
+ pA->pBefore = pP;
|
||
+ pP->pUp = pA;
|
||
+ pP->pAfter = pY;
|
||
+ if( pY ) pY->pUp = pP;
|
||
+ closureAvlRecomputeHeight(pP);
|
||
+ closureAvlRecomputeHeight(pA);
|
||
+ return pA;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return a pointer to the pBefore or pAfter pointer in the parent
|
||
+** of p that points to p. Or if p is the root node, return pp.
|
||
+*/
|
||
+static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
|
||
+ closure_avl *pUp = p->pUp;
|
||
+ if( pUp==0 ) return pp;
|
||
+ if( pUp->pAfter==p ) return &pUp->pAfter;
|
||
+ return &pUp->pBefore;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Rebalance all nodes starting with p and working up to the root.
|
||
+** Return the new root.
|
||
+*/
|
||
+static closure_avl *closureAvlBalance(closure_avl *p){
|
||
+ closure_avl *pTop = p;
|
||
+ closure_avl **pp;
|
||
+ while( p ){
|
||
+ closureAvlRecomputeHeight(p);
|
||
+ if( p->imbalance>=2 ){
|
||
+ closure_avl *pB = p->pBefore;
|
||
+ if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
|
||
+ pp = closureAvlFromPtr(p,&p);
|
||
+ p = *pp = closureAvlRotateBefore(p);
|
||
+ }else if( p->imbalance<=(-2) ){
|
||
+ closure_avl *pA = p->pAfter;
|
||
+ if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
|
||
+ pp = closureAvlFromPtr(p,&p);
|
||
+ p = *pp = closureAvlRotateAfter(p);
|
||
+ }
|
||
+ pTop = p;
|
||
+ p = p->pUp;
|
||
+ }
|
||
+ return pTop;
|
||
+}
|
||
+
|
||
+/* Search the tree rooted at p for an entry with id. Return a pointer
|
||
+** to the entry or return NULL.
|
||
+*/
|
||
+static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
|
||
+ while( p && id!=p->id ){
|
||
+ p = (id<p->id) ? p->pBefore : p->pAfter;
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
+
|
||
+/* Find the first node (the one with the smallest key).
|
||
+*/
|
||
+static closure_avl *closureAvlFirst(closure_avl *p){
|
||
+ if( p ) while( p->pBefore ) p = p->pBefore;
|
||
+ return p;
|
||
+}
|
||
+
|
||
+/* Return the node with the next larger key after p.
|
||
+*/
|
||
+closure_avl *closureAvlNext(closure_avl *p){
|
||
+ closure_avl *pPrev = 0;
|
||
+ while( p && p->pAfter==pPrev ){
|
||
+ pPrev = p;
|
||
+ p = p->pUp;
|
||
+ }
|
||
+ if( p && pPrev==0 ){
|
||
+ p = closureAvlFirst(p->pAfter);
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
+
|
||
+/* Insert a new node pNew. Return NULL on success. If the key is not
|
||
+** unique, then do not perform the insert but instead leave pNew unchanged
|
||
+** and return a pointer to an existing node with the same key.
|
||
+*/
|
||
+static closure_avl *closureAvlInsert(
|
||
+ closure_avl **ppHead, /* Head of the tree */
|
||
+ closure_avl *pNew /* New node to be inserted */
|
||
+){
|
||
+ closure_avl *p = *ppHead;
|
||
+ if( p==0 ){
|
||
+ p = pNew;
|
||
+ pNew->pUp = 0;
|
||
+ }else{
|
||
+ while( p ){
|
||
+ if( pNew->id<p->id ){
|
||
+ if( p->pBefore ){
|
||
+ p = p->pBefore;
|
||
+ }else{
|
||
+ p->pBefore = pNew;
|
||
+ pNew->pUp = p;
|
||
+ break;
|
||
+ }
|
||
+ }else if( pNew->id>p->id ){
|
||
+ if( p->pAfter ){
|
||
+ p = p->pAfter;
|
||
+ }else{
|
||
+ p->pAfter = pNew;
|
||
+ pNew->pUp = p;
|
||
+ break;
|
||
+ }
|
||
+ }else{
|
||
+ return p;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ pNew->pBefore = 0;
|
||
+ pNew->pAfter = 0;
|
||
+ pNew->height = 1;
|
||
+ pNew->imbalance = 0;
|
||
+ *ppHead = closureAvlBalance(p);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Walk the tree can call xDestroy on each node
|
||
+*/
|
||
+static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
|
||
+ if( p ){
|
||
+ closureAvlDestroy(p->pBefore, xDestroy);
|
||
+ closureAvlDestroy(p->pAfter, xDestroy);
|
||
+ xDestroy(p);
|
||
+ }
|
||
+}
|
||
+/*
|
||
+** End of the AVL Tree implementation
|
||
+******************************************************************************/
|
||
+
|
||
+/*
|
||
+** A closure virtual-table object
|
||
+*/
|
||
+struct closure_vtab {
|
||
+ sqlite3_vtab base; /* Base class - must be first */
|
||
+ char *zDb; /* Name of database. (ex: "main") */
|
||
+ char *zSelf; /* Name of this virtual table */
|
||
+ char *zTableName; /* Name of table holding parent/child relation */
|
||
+ char *zIdColumn; /* Name of ID column of zTableName */
|
||
+ char *zParentColumn; /* Name of PARENT column in zTableName */
|
||
+ sqlite3 *db; /* The database connection */
|
||
+ int nCursor; /* Number of pending cursors */
|
||
+};
|
||
+
|
||
+/* A closure cursor object */
|
||
+struct closure_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ closure_vtab *pVtab; /* The virtual table this cursor belongs to */
|
||
+ char *zTableName; /* Name of table holding parent/child relation */
|
||
+ char *zIdColumn; /* Name of ID column of zTableName */
|
||
+ char *zParentColumn; /* Name of PARENT column in zTableName */
|
||
+ closure_avl *pCurrent; /* Current element of output */
|
||
+ closure_avl *pClosure; /* The complete closure tree */
|
||
+};
|
||
+
|
||
+/* A queue of AVL nodes */
|
||
+struct closure_queue {
|
||
+ closure_avl *pFirst; /* Oldest node on the queue */
|
||
+ closure_avl *pLast; /* Youngest node on the queue */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Add a node to the end of the queue
|
||
+*/
|
||
+static void queuePush(closure_queue *pQueue, closure_avl *pNode){
|
||
+ pNode->pList = 0;
|
||
+ if( pQueue->pLast ){
|
||
+ pQueue->pLast->pList = pNode;
|
||
+ }else{
|
||
+ pQueue->pFirst = pNode;
|
||
+ }
|
||
+ pQueue->pLast = pNode;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Extract the oldest element (the front element) from the queue.
|
||
+*/
|
||
+static closure_avl *queuePull(closure_queue *pQueue){
|
||
+ closure_avl *p = pQueue->pFirst;
|
||
+ if( p ){
|
||
+ pQueue->pFirst = p->pList;
|
||
+ if( pQueue->pFirst==0 ) pQueue->pLast = 0;
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This function converts an SQL quoted string into an unquoted string
|
||
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
||
+** containing the result. The caller should eventually free this buffer
|
||
+** using sqlite3_free.
|
||
+**
|
||
+** Examples:
|
||
+**
|
||
+** "abc" becomes abc
|
||
+** 'xyz' becomes xyz
|
||
+** [pqr] becomes pqr
|
||
+** `mno` becomes mno
|
||
+*/
|
||
+static char *closureDequote(const char *zIn){
|
||
+ int nIn; /* Size of input string, in bytes */
|
||
+ char *zOut; /* Output (dequoted) string */
|
||
+
|
||
+ nIn = (int)strlen(zIn);
|
||
+ zOut = sqlite3_malloc(nIn+1);
|
||
+ if( zOut ){
|
||
+ char q = zIn[0]; /* Quote character (if any ) */
|
||
+
|
||
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
||
+ memcpy(zOut, zIn, nIn+1);
|
||
+ }else{
|
||
+ int iOut = 0; /* Index of next byte to write to output */
|
||
+ int iIn; /* Index of next byte to read from input */
|
||
+
|
||
+ if( q=='[' ) q = ']';
|
||
+ for(iIn=1; iIn<nIn; iIn++){
|
||
+ if( zIn[iIn]==q ) iIn++;
|
||
+ zOut[iOut++] = zIn[iIn];
|
||
+ }
|
||
+ }
|
||
+ assert( (int)strlen(zOut)<=nIn );
|
||
+ }
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Deallocate an closure_vtab object
|
||
+*/
|
||
+static void closureFree(closure_vtab *p){
|
||
+ if( p ){
|
||
+ sqlite3_free(p->zDb);
|
||
+ sqlite3_free(p->zSelf);
|
||
+ sqlite3_free(p->zTableName);
|
||
+ sqlite3_free(p->zIdColumn);
|
||
+ sqlite3_free(p->zParentColumn);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+ sqlite3_free(p);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** xDisconnect/xDestroy method for the closure module.
|
||
+*/
|
||
+static int closureDisconnect(sqlite3_vtab *pVtab){
|
||
+ closure_vtab *p = (closure_vtab*)pVtab;
|
||
+ assert( p->nCursor==0 );
|
||
+ closureFree(p);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Check to see if the argument is of the form:
|
||
+**
|
||
+** KEY = VALUE
|
||
+**
|
||
+** If it is, return a pointer to the first character of VALUE.
|
||
+** If not, return NULL. Spaces around the = are ignored.
|
||
+*/
|
||
+static const char *closureValueOfKey(const char *zKey, const char *zStr){
|
||
+ int nKey = (int)strlen(zKey);
|
||
+ int nStr = (int)strlen(zStr);
|
||
+ int i;
|
||
+ if( nStr<nKey+1 ) return 0;
|
||
+ if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
||
+ for(i=nKey; isspace(zStr[i]); i++){}
|
||
+ if( zStr[i]!='=' ) return 0;
|
||
+ i++;
|
||
+ while( isspace(zStr[i]) ){ i++; }
|
||
+ return zStr+i;
|
||
+}
|
||
+
|
||
+/*
|
||
+** xConnect/xCreate method for the closure module. Arguments are:
|
||
+**
|
||
+** argv[0] -> module name ("transitive_closure")
|
||
+** argv[1] -> database name
|
||
+** argv[2] -> table name
|
||
+** argv[3...] -> arguments
|
||
+*/
|
||
+static int closureConnect(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ closure_vtab *pNew = 0; /* New virtual table */
|
||
+ const char *zDb = argv[1];
|
||
+ const char *zVal;
|
||
+ int i;
|
||
+
|
||
+ (void)pAux;
|
||
+ *ppVtab = 0;
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ rc = SQLITE_NOMEM;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->db = db;
|
||
+ pNew->zDb = sqlite3_mprintf("%s", zDb);
|
||
+ if( pNew->zDb==0 ) goto closureConnectError;
|
||
+ pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
||
+ if( pNew->zSelf==0 ) goto closureConnectError;
|
||
+ for(i=3; i<argc; i++){
|
||
+ zVal = closureValueOfKey("tablename", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zTableName);
|
||
+ pNew->zTableName = closureDequote(zVal);
|
||
+ if( pNew->zTableName==0 ) goto closureConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ zVal = closureValueOfKey("idcolumn", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zIdColumn);
|
||
+ pNew->zIdColumn = closureDequote(zVal);
|
||
+ if( pNew->zIdColumn==0 ) goto closureConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ zVal = closureValueOfKey("parentcolumn", argv[i]);
|
||
+ if( zVal ){
|
||
+ sqlite3_free(pNew->zParentColumn);
|
||
+ pNew->zParentColumn = closureDequote(zVal);
|
||
+ if( pNew->zParentColumn==0 ) goto closureConnectError;
|
||
+ continue;
|
||
+ }
|
||
+ *pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
||
+ closureFree(pNew);
|
||
+ *ppVtab = 0;
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ rc = sqlite3_declare_vtab(db,
|
||
+ "CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
|
||
+ "idcolumn HIDDEN,parentcolumn HIDDEN)"
|
||
+ );
|
||
+#define CLOSURE_COL_ID 0
|
||
+#define CLOSURE_COL_DEPTH 1
|
||
+#define CLOSURE_COL_ROOT 2
|
||
+#define CLOSURE_COL_TABLENAME 3
|
||
+#define CLOSURE_COL_IDCOLUMN 4
|
||
+#define CLOSURE_COL_PARENTCOLUMN 5
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ closureFree(pNew);
|
||
+ }
|
||
+ *ppVtab = &pNew->base;
|
||
+ return rc;
|
||
+
|
||
+closureConnectError:
|
||
+ closureFree(pNew);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open a new closure cursor.
|
||
+*/
|
||
+static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||
+ closure_vtab *p = (closure_vtab*)pVTab;
|
||
+ closure_cursor *pCur;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ pCur->pVtab = p;
|
||
+ *ppCursor = &pCur->base;
|
||
+ p->nCursor++;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
||
+** to indicate that it is at EOF.
|
||
+*/
|
||
+static void closureClearCursor(closure_cursor *pCur){
|
||
+ closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
|
||
+ sqlite3_free(pCur->zTableName);
|
||
+ sqlite3_free(pCur->zIdColumn);
|
||
+ sqlite3_free(pCur->zParentColumn);
|
||
+ pCur->zTableName = 0;
|
||
+ pCur->zIdColumn = 0;
|
||
+ pCur->zParentColumn = 0;
|
||
+ pCur->pCurrent = 0;
|
||
+ pCur->pClosure = 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Close a closure cursor.
|
||
+*/
|
||
+static int closureClose(sqlite3_vtab_cursor *cur){
|
||
+ closure_cursor *pCur = (closure_cursor *)cur;
|
||
+ closureClearCursor(pCur);
|
||
+ pCur->pVtab->nCursor--;
|
||
+ sqlite3_free(pCur);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Advance a cursor to its next row of output
|
||
+*/
|
||
+static int closureNext(sqlite3_vtab_cursor *cur){
|
||
+ closure_cursor *pCur = (closure_cursor*)cur;
|
||
+ pCur->pCurrent = closureAvlNext(pCur->pCurrent);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Allocate and insert a node
|
||
+*/
|
||
+static int closureInsertNode(
|
||
+ closure_queue *pQueue, /* Add new node to this queue */
|
||
+ closure_cursor *pCur, /* The cursor into which to add the node */
|
||
+ sqlite3_int64 id, /* The node ID */
|
||
+ int iGeneration /* The generation number for this node */
|
||
+){
|
||
+ closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->id = id;
|
||
+ pNew->iGeneration = iGeneration;
|
||
+ closureAvlInsert(&pCur->pClosure, pNew);
|
||
+ queuePush(pQueue, pNew);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Called to "rewind" a cursor back to the beginning so that
|
||
+** it starts its output over again. Always called at least once
|
||
+** prior to any closureColumn, closureRowid, or closureEof call.
|
||
+**
|
||
+** This routine actually computes the closure.
|
||
+**
|
||
+** See the comment at the beginning of closureBestIndex() for a
|
||
+** description of the meaning of idxNum. The idxStr parameter is
|
||
+** not used.
|
||
+*/
|
||
+static int closureFilter(
|
||
+ sqlite3_vtab_cursor *pVtabCursor,
|
||
+ int idxNum, const char *idxStr,
|
||
+ int argc, sqlite3_value **argv
|
||
+){
|
||
+ closure_cursor *pCur = (closure_cursor *)pVtabCursor;
|
||
+ closure_vtab *pVtab = pCur->pVtab;
|
||
+ sqlite3_int64 iRoot;
|
||
+ int mxGen = 999999999;
|
||
+ char *zSql;
|
||
+ sqlite3_stmt *pStmt;
|
||
+ closure_avl *pAvl;
|
||
+ int rc = SQLITE_OK;
|
||
+ const char *zTableName = pVtab->zTableName;
|
||
+ const char *zIdColumn = pVtab->zIdColumn;
|
||
+ const char *zParentColumn = pVtab->zParentColumn;
|
||
+ closure_queue sQueue;
|
||
+
|
||
+ (void)idxStr; /* Unused parameter */
|
||
+ (void)argc; /* Unused parameter */
|
||
+ closureClearCursor(pCur);
|
||
+ memset(&sQueue, 0, sizeof(sQueue));
|
||
+ if( (idxNum & 1)==0 ){
|
||
+ /* No root=$root in the WHERE clause. Return an empty set */
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ iRoot = sqlite3_value_int64(argv[0]);
|
||
+ if( (idxNum & 0x000f0)!=0 ){
|
||
+ mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
|
||
+ if( (idxNum & 0x00002)!=0 ) mxGen--;
|
||
+ }
|
||
+ if( (idxNum & 0x00f00)!=0 ){
|
||
+ zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
|
||
+ pCur->zTableName = sqlite3_mprintf("%s", zTableName);
|
||
+ }
|
||
+ if( (idxNum & 0x0f000)!=0 ){
|
||
+ zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
|
||
+ pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
|
||
+ }
|
||
+ if( (idxNum & 0x0f0000)!=0 ){
|
||
+ zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
|
||
+ pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
|
||
+ }
|
||
+
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
|
||
+ zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
|
||
+ if( zSql==0 ){
|
||
+ return SQLITE_NOMEM;
|
||
+ }else{
|
||
+ rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ if( rc ){
|
||
+ sqlite3_free(pVtab->base.zErrMsg);
|
||
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
|
||
+ return rc;
|
||
+ }
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
|
||
+ }
|
||
+ while( (pAvl = queuePull(&sQueue))!=0 ){
|
||
+ if( pAvl->iGeneration>=mxGen ) continue;
|
||
+ sqlite3_bind_int64(pStmt, 1, pAvl->id);
|
||
+ while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||
+ if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
|
||
+ sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
|
||
+ if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
|
||
+ rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ sqlite3_reset(pStmt);
|
||
+ }
|
||
+ sqlite3_finalize(pStmt);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ pCur->pCurrent = closureAvlFirst(pCur->pClosure);
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Only the word and distance columns have values. All other columns
|
||
+** return NULL
|
||
+*/
|
||
+static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||
+ closure_cursor *pCur = (closure_cursor*)cur;
|
||
+ switch( i ){
|
||
+ case CLOSURE_COL_ID: {
|
||
+ sqlite3_result_int64(ctx, pCur->pCurrent->id);
|
||
+ break;
|
||
+ }
|
||
+ case CLOSURE_COL_DEPTH: {
|
||
+ sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
|
||
+ break;
|
||
+ }
|
||
+ case CLOSURE_COL_ROOT: {
|
||
+ sqlite3_result_null(ctx);
|
||
+ break;
|
||
+ }
|
||
+ case CLOSURE_COL_TABLENAME: {
|
||
+ sqlite3_result_text(ctx,
|
||
+ pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
|
||
+ -1, SQLITE_TRANSIENT);
|
||
+ break;
|
||
+ }
|
||
+ case CLOSURE_COL_IDCOLUMN: {
|
||
+ sqlite3_result_text(ctx,
|
||
+ pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
|
||
+ -1, SQLITE_TRANSIENT);
|
||
+ break;
|
||
+ }
|
||
+ case CLOSURE_COL_PARENTCOLUMN: {
|
||
+ sqlite3_result_text(ctx,
|
||
+ pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
|
||
+ -1, SQLITE_TRANSIENT);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The rowid. For the closure table, this is the same as the "id" column.
|
||
+*/
|
||
+static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||
+ closure_cursor *pCur = (closure_cursor*)cur;
|
||
+ *pRowid = pCur->pCurrent->id;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** EOF indicator
|
||
+*/
|
||
+static int closureEof(sqlite3_vtab_cursor *cur){
|
||
+ closure_cursor *pCur = (closure_cursor*)cur;
|
||
+ return pCur->pCurrent==0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Search for terms of these forms:
|
||
+**
|
||
+** (A) root = $root
|
||
+** (B1) depth < $depth
|
||
+** (B2) depth <= $depth
|
||
+** (B3) depth = $depth
|
||
+** (C) tablename = $tablename
|
||
+** (D) idcolumn = $idcolumn
|
||
+** (E) parentcolumn = $parentcolumn
|
||
+**
|
||
+**
|
||
+**
|
||
+** idxNum meaning
|
||
+** ---------- ------------------------------------------------------
|
||
+** 0x00000001 Term of the form (A) found
|
||
+** 0x00000002 The term of bit-2 is like (B1)
|
||
+** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
|
||
+** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
|
||
+** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
|
||
+** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
|
||
+**
|
||
+** There must be a term of type (A). If there is not, then the index type
|
||
+** is 0 and the query will return an empty set.
|
||
+*/
|
||
+static int closureBestIndex(
|
||
+ sqlite3_vtab *pTab, /* The virtual table */
|
||
+ sqlite3_index_info *pIdxInfo /* Information about the query */
|
||
+){
|
||
+ int iPlan = 0;
|
||
+ int i;
|
||
+ int idx = 1;
|
||
+ int seenMatch = 0;
|
||
+ const struct sqlite3_index_constraint *pConstraint;
|
||
+ closure_vtab *pVtab = (closure_vtab*)pTab;
|
||
+ double rCost = 10000000.0;
|
||
+
|
||
+ pConstraint = pIdxInfo->aConstraint;
|
||
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||
+ if( pConstraint->iColumn==CLOSURE_COL_ROOT
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||
+ seenMatch = 1;
|
||
+ }
|
||
+ if( pConstraint->usable==0 ) continue;
|
||
+ if( (iPlan & 1)==0
|
||
+ && pConstraint->iColumn==CLOSURE_COL_ROOT
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 1;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ rCost /= 100.0;
|
||
+ }
|
||
+ if( (iPlan & 0x0000f0)==0
|
||
+ && pConstraint->iColumn==CLOSURE_COL_DEPTH
|
||
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
|
||
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
|
||
+ ){
|
||
+ iPlan |= idx<<4;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
|
||
+ rCost /= 5.0;
|
||
+ }
|
||
+ if( (iPlan & 0x000f00)==0
|
||
+ && pConstraint->iColumn==CLOSURE_COL_TABLENAME
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= idx<<8;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ rCost /= 5.0;
|
||
+ }
|
||
+ if( (iPlan & 0x00f000)==0
|
||
+ && pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= idx<<12;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ }
|
||
+ if( (iPlan & 0x0f0000)==0
|
||
+ && pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= idx<<16;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ }
|
||
+ }
|
||
+ if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
|
||
+ || (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
|
||
+ || (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
|
||
+ ){
|
||
+ /* All of tablename, idcolumn, and parentcolumn must be specified
|
||
+ ** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
|
||
+ ** or else the result is an empty set. */
|
||
+ iPlan = 0;
|
||
+ }
|
||
+ pIdxInfo->idxNum = iPlan;
|
||
+ if( pIdxInfo->nOrderBy==1
|
||
+ && pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
|
||
+ && pIdxInfo->aOrderBy[0].desc==0
|
||
+ ){
|
||
+ pIdxInfo->orderByConsumed = 1;
|
||
+ }
|
||
+ if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
|
||
+ pIdxInfo->estimatedCost = rCost;
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** A virtual table module that implements the "transitive_closure".
|
||
+*/
|
||
+static sqlite3_module closureModule = {
|
||
+ 0, /* iVersion */
|
||
+ closureConnect, /* xCreate */
|
||
+ closureConnect, /* xConnect */
|
||
+ closureBestIndex, /* xBestIndex */
|
||
+ closureDisconnect, /* xDisconnect */
|
||
+ closureDisconnect, /* xDestroy */
|
||
+ closureOpen, /* xOpen - open a cursor */
|
||
+ closureClose, /* xClose - close a cursor */
|
||
+ closureFilter, /* xFilter - configure scan constraints */
|
||
+ closureNext, /* xNext - advance a cursor */
|
||
+ closureEof, /* xEof - check for end of scan */
|
||
+ closureColumn, /* xColumn - read data */
|
||
+ closureRowid, /* xRowid - read data */
|
||
+ 0, /* xUpdate */
|
||
+ 0, /* xBegin */
|
||
+ 0, /* xSync */
|
||
+ 0, /* xCommit */
|
||
+ 0, /* xRollback */
|
||
+ 0, /* xFindMethod */
|
||
+ 0, /* xRename */
|
||
+ 0, /* xSavepoint */
|
||
+ 0, /* xRelease */
|
||
+ 0 /* xRollbackTo */
|
||
+};
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+/*
|
||
+** Register the closure virtual table
|
||
+*/
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_closure_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg;
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg;
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/compress.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/compress.c 2015-01-31 00:31:56.365141400 +0100
|
||
@@ -0,0 +1,125 @@
|
||
+/*
|
||
+** 2014-06-13
|
||
+**
|
||
+** 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 SQLite extension implements SQL compression functions
|
||
+** compress() and uncompress() using ZLIB.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <zlib.h>
|
||
+
|
||
+/*
|
||
+** Implementation of the "compress(X)" SQL function. The input X is
|
||
+** compressed using zLib and the output is returned.
|
||
+**
|
||
+** The output is a BLOB that begins with a variable-length integer that
|
||
+** is the input size in bytes (the size of X before compression). The
|
||
+** variable-length integer is implemented as 1 to 5 bytes. There are
|
||
+** seven bits per integer stored in the lower seven bits of each byte.
|
||
+** More significant bits occur first. The most significant bit (0x80)
|
||
+** is a flag to indicate the end of the integer.
|
||
+*/
|
||
+static void compressFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *pIn;
|
||
+ unsigned char *pOut;
|
||
+ unsigned int nIn;
|
||
+ unsigned long int nOut;
|
||
+ unsigned char x[8];
|
||
+ int i, j;
|
||
+
|
||
+ pIn = sqlite3_value_blob(argv[0]);
|
||
+ nIn = sqlite3_value_bytes(argv[0]);
|
||
+ nOut = 13 + nIn + (nIn+999)/1000;
|
||
+ pOut = sqlite3_malloc( nOut+5 );
|
||
+ for(i=4; i>=0; i--){
|
||
+ x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||
+ }
|
||
+ for(i=0; i<4 && x[i]==0; i++){}
|
||
+ for(j=0; i<=4; i++, j++) pOut[j] = x[i];
|
||
+ pOut[j-1] |= 0x80;
|
||
+ compress(&pOut[j], &nOut, pIn, nIn);
|
||
+ sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the "uncompress(X)" SQL function. The argument X
|
||
+** is a blob which was obtained from compress(Y). The output will be
|
||
+** the value Y.
|
||
+*/
|
||
+static void uncompressFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *pIn;
|
||
+ unsigned char *pOut;
|
||
+ unsigned int nIn;
|
||
+ unsigned long int nOut;
|
||
+ int rc;
|
||
+ int i;
|
||
+
|
||
+ pIn = sqlite3_value_blob(argv[0]);
|
||
+ nIn = sqlite3_value_bytes(argv[0]);
|
||
+ nOut = 0;
|
||
+ for(i=0; i<nIn && i<5; i++){
|
||
+ nOut = (nOut<<7) | (pIn[i]&0x7f);
|
||
+ if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||
+ }
|
||
+ pOut = sqlite3_malloc( nOut+1 );
|
||
+ rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||
+ if( rc==Z_OK ){
|
||
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_compress_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
|
||
+ compressFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
||
+ uncompressFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
|
||
+ compressFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
|
||
+ uncompressFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/eval.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/eval.c 2015-01-31 00:31:56.372141800 +0100
|
||
@@ -0,0 +1,137 @@
|
||
+/*
|
||
+** 2014-11-10
|
||
+**
|
||
+** 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 SQLite extension implements SQL function eval() which runs
|
||
+** SQL statements recursively.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <string.h>
|
||
+
|
||
+/*
|
||
+** Structure used to accumulate the output
|
||
+*/
|
||
+struct EvalResult {
|
||
+ char *z; /* Accumulated output */
|
||
+ const char *zSep; /* Separator */
|
||
+ int szSep; /* Size of the separator string */
|
||
+ int nAlloc; /* Number of bytes allocated for z[] */
|
||
+ int nUsed; /* Number of bytes of z[] actually used */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Callback from sqlite_exec() for the eval() function.
|
||
+*/
|
||
+static int callback(void *pCtx, int argc, char **argv, char **colnames){
|
||
+ struct EvalResult *p = (struct EvalResult*)pCtx;
|
||
+ int i;
|
||
+ for(i=0; i<argc; i++){
|
||
+ const char *z = argv[i] ? argv[i] : "";
|
||
+ size_t sz = strlen(z);
|
||
+ if( sz+p->nUsed+p->szSep+1 > p->nAlloc ){
|
||
+ char *zNew;
|
||
+ p->nAlloc = p->nAlloc*2 + sz + p->szSep + 1;
|
||
+ zNew = sqlite3_realloc(p->z, p->nAlloc);
|
||
+ if( zNew==0 ){
|
||
+ sqlite3_free(p->z);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+ return 1;
|
||
+ }
|
||
+ p->z = zNew;
|
||
+ }
|
||
+ if( p->nUsed>0 ){
|
||
+ memcpy(&p->z[p->nUsed], p->zSep, p->szSep);
|
||
+ p->nUsed += p->szSep;
|
||
+ }
|
||
+ memcpy(&p->z[p->nUsed], z, sz);
|
||
+ p->nUsed += sz;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the eval(X) and eval(X,Y) SQL functions.
|
||
+**
|
||
+** Evaluate the SQL text in X. Return the results, using string
|
||
+** Y as the separator. If Y is omitted, use a single space character.
|
||
+*/
|
||
+static void sqlEvalFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const char *zSql;
|
||
+ sqlite3 *db;
|
||
+ char *zErr = 0;
|
||
+ int rc;
|
||
+ struct EvalResult x;
|
||
+
|
||
+ memset(&x, 0, sizeof(x));
|
||
+ x.zSep = " ";
|
||
+ zSql = (const char*)sqlite3_value_text(argv[0]);
|
||
+ if( zSql==0 ) return;
|
||
+ if( argc>1 ){
|
||
+ x.zSep = (const char*)sqlite3_value_text(argv[1]);
|
||
+ if( x.zSep==0 ) return;
|
||
+ }
|
||
+ x.szSep = (int)strlen(x.zSep);
|
||
+ db = sqlite3_context_db_handle(context);
|
||
+ rc = sqlite3_exec(db, zSql, callback, &x, &zErr);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ sqlite3_result_error(context, zErr, -1);
|
||
+ sqlite3_free(zErr);
|
||
+ }else if( x.zSep==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ sqlite3_free(x.z);
|
||
+ }else{
|
||
+ sqlite3_result_text(context, x.z, x.nUsed, sqlite3_free);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_eval_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
||
+ sqlEvalFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
||
+ sqlEvalFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "eval", 1, SQLITE_UTF8, 0,
|
||
+ sqlEvalFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "eval", 2, SQLITE_UTF8, 0,
|
||
+ sqlEvalFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/fileio.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/fileio.c 2015-01-31 00:31:56.379142200 +0100
|
||
@@ -0,0 +1,118 @@
|
||
+/*
|
||
+** 2014-06-13
|
||
+**
|
||
+** 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 SQLite extension implements SQL functions readfile() and
|
||
+** writefile().
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <stdio.h>
|
||
+
|
||
+/*
|
||
+** Implementation of the "readfile(X)" SQL function. The entire content
|
||
+** of the file named X is read and returned as a BLOB. NULL is returned
|
||
+** if the file does not exist or is unreadable.
|
||
+*/
|
||
+static void readfileFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const char *zName;
|
||
+ FILE *in;
|
||
+ long nIn;
|
||
+ void *pBuf;
|
||
+
|
||
+ zName = (const char*)sqlite3_value_text(argv[0]);
|
||
+ if( zName==0 ) return;
|
||
+ in = fopen(zName, "rb");
|
||
+ if( in==0 ) return;
|
||
+ fseek(in, 0, SEEK_END);
|
||
+ nIn = ftell(in);
|
||
+ rewind(in);
|
||
+ pBuf = sqlite3_malloc( nIn );
|
||
+ if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
|
||
+ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
|
||
+ }else{
|
||
+ sqlite3_free(pBuf);
|
||
+ }
|
||
+ fclose(in);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
||
+** is written into file X. The number of bytes written is returned. Or
|
||
+** NULL is returned if something goes wrong, such as being unable to open
|
||
+** file X for writing.
|
||
+*/
|
||
+static void writefileFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ FILE *out;
|
||
+ const char *z;
|
||
+ sqlite3_int64 rc;
|
||
+ const char *zFile;
|
||
+
|
||
+ zFile = (const char*)sqlite3_value_text(argv[0]);
|
||
+ if( zFile==0 ) return;
|
||
+ out = fopen(zFile, "wb");
|
||
+ if( out==0 ) return;
|
||
+ z = (const char*)sqlite3_value_blob(argv[1]);
|
||
+ if( z==0 ){
|
||
+ rc = 0;
|
||
+ }else{
|
||
+ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
|
||
+ }
|
||
+ fclose(out);
|
||
+ sqlite3_result_int64(context, rc);
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_fileio_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
||
+ readfileFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||
+ writefileFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
|
||
+ readfileFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||
+ writefileFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/fuzzer.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/fuzzer.c 2015-01-31 00:31:56.393143000 +0100
|
||
@@ -0,0 +1,1198 @@
|
||
+/*
|
||
+** 2011 March 24
|
||
+**
|
||
+** 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.
|
||
+**
|
||
+*************************************************************************
|
||
+**
|
||
+** Code for a demonstration virtual table that generates variations
|
||
+** on an input word at increasing edit distances from the original.
|
||
+**
|
||
+** A fuzzer virtual table is created like this:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
|
||
+**
|
||
+** When it is created, the new fuzzer table must be supplied with the
|
||
+** name of a "fuzzer data table", which must reside in the same database
|
||
+** file as the new fuzzer table. The fuzzer data table contains the various
|
||
+** transformations and their costs that the fuzzer logic uses to generate
|
||
+** variations.
|
||
+**
|
||
+** The fuzzer data table must contain exactly four columns (more precisely,
|
||
+** the statement "SELECT * FROM <fuzzer_data_table>" must return records
|
||
+** that consist of four columns). It does not matter what the columns are
|
||
+** named.
|
||
+**
|
||
+** Each row in the fuzzer data table represents a single character
|
||
+** transformation. The left most column of the row (column 0) contains an
|
||
+** integer value - the identifier of the ruleset to which the transformation
|
||
+** rule belongs (see "MULTIPLE RULE SETS" below). The second column of the
|
||
+** row (column 0) contains the input character or characters. The third
|
||
+** column contains the output character or characters. And the fourth column
|
||
+** contains the integer cost of making the transformation. For example:
|
||
+**
|
||
+** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
|
||
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
|
||
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
|
||
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
|
||
+** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
|
||
+**
|
||
+** The first row inserted into the fuzzer data table by the SQL script
|
||
+** above indicates that the cost of inserting a letter 'a' is 100. (All
|
||
+** costs are integers. We recommend that costs be scaled so that the
|
||
+** average cost is around 100.) The second INSERT statement creates a rule
|
||
+** saying that the cost of deleting a single letter 'b' is 87. The third
|
||
+** and fourth INSERT statements mean that the cost of transforming a
|
||
+** single letter "o" into the two-letter sequence "oe" is 38 and that the
|
||
+** cost of transforming "oe" back into "o" is 40.
|
||
+**
|
||
+** The contents of the fuzzer data table are loaded into main memory when
|
||
+** a fuzzer table is first created, and may be internally reloaded by the
|
||
+** system at any subsequent time. Therefore, the fuzzer data table should be
|
||
+** populated before the fuzzer table is created and not modified thereafter.
|
||
+** If you do need to modify the contents of the fuzzer data table, it is
|
||
+** recommended that the associated fuzzer table be dropped, the fuzzer data
|
||
+** table edited, and the fuzzer table recreated within a single transaction.
|
||
+** Alternatively, the fuzzer data table can be edited then the database
|
||
+** connection can be closed and reopened.
|
||
+**
|
||
+** Once it has been created, the fuzzer table can be queried as follows:
|
||
+**
|
||
+** SELECT word, distance FROM f
|
||
+** WHERE word MATCH 'abcdefg'
|
||
+** AND distance<200;
|
||
+**
|
||
+** This first query outputs the string "abcdefg" and all strings that
|
||
+** can be derived from that string by appling the specified transformations.
|
||
+** The strings are output together with their total transformation cost
|
||
+** (called "distance") and appear in order of increasing cost. No string
|
||
+** is output more than once. If there are multiple ways to transform the
|
||
+** target string into the output string then the lowest cost transform is
|
||
+** the one that is returned. In the example, the search is limited to
|
||
+** strings with a total distance of less than 200.
|
||
+**
|
||
+** The fuzzer is a read-only table. Any attempt to DELETE, INSERT, or
|
||
+** UPDATE on a fuzzer table will throw an error.
|
||
+**
|
||
+** It is important to put some kind of a limit on the fuzzer output. This
|
||
+** can be either in the form of a LIMIT clause at the end of the query,
|
||
+** or better, a "distance<NNN" constraint where NNN is some number. The
|
||
+** running time and memory requirement is exponential in the value of NNN
|
||
+** so you want to make sure that NNN is not too big. A value of NNN that
|
||
+** is about twice the average transformation cost seems to give good results.
|
||
+**
|
||
+** The fuzzer table can be useful for tasks such as spelling correction.
|
||
+** Suppose there is a second table vocabulary(w) where the w column contains
|
||
+** all correctly spelled words. Let $word be a word you want to look up.
|
||
+**
|
||
+** SELECT vocabulary.w FROM f, vocabulary
|
||
+** WHERE f.word MATCH $word
|
||
+** AND f.distance<=200
|
||
+** AND f.word=vocabulary.w
|
||
+** LIMIT 20
|
||
+**
|
||
+** The query above gives the 20 closest words to the $word being tested.
|
||
+** (Note that for good performance, the vocubulary.w column should be
|
||
+** indexed.)
|
||
+**
|
||
+** A similar query can be used to find all words in the dictionary that
|
||
+** begin with some prefix $prefix:
|
||
+**
|
||
+** SELECT vocabulary.w FROM f, vocabulary
|
||
+** WHERE f.word MATCH $prefix
|
||
+** AND f.distance<=200
|
||
+** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF')
|
||
+** LIMIT 50
|
||
+**
|
||
+** This last query will show up to 50 words out of the vocabulary that
|
||
+** match or nearly match the $prefix.
|
||
+**
|
||
+** MULTIPLE RULE SETS
|
||
+**
|
||
+** Normally, the "ruleset" value associated with all character transformations
|
||
+** in the fuzzer data table is zero. However, if required, the fuzzer table
|
||
+** allows multiple rulesets to be defined. Each query uses only a single
|
||
+** ruleset. This allows, for example, a single fuzzer table to support
|
||
+** multiple languages.
|
||
+**
|
||
+** By default, only the rules from ruleset 0 are used. To specify an
|
||
+** alternative ruleset, a "ruleset = ?" expression must be added to the
|
||
+** WHERE clause of a SELECT, where ? is the identifier of the desired
|
||
+** ruleset. For example:
|
||
+**
|
||
+** SELECT vocabulary.w FROM f, vocabulary
|
||
+** WHERE f.word MATCH $word
|
||
+** AND f.distance<=200
|
||
+** AND f.word=vocabulary.w
|
||
+** AND f.ruleset=1 -- Specify the ruleset to use here
|
||
+** LIMIT 20
|
||
+**
|
||
+** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset
|
||
+** 0 is used.
|
||
+**
|
||
+** LIMITS
|
||
+**
|
||
+** The maximum ruleset number is 2147483647. The maximum length of either
|
||
+** of the strings in the second or third column of the fuzzer data table
|
||
+** is 50 bytes. The maximum cost on a rule is 1000.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+
|
||
+/* If SQLITE_DEBUG is not defined, disable assert statements. */
|
||
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
|
||
+# define NDEBUG
|
||
+#endif
|
||
+
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <assert.h>
|
||
+#include <stdio.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+/*
|
||
+** Forward declaration of objects used by this implementation
|
||
+*/
|
||
+typedef struct fuzzer_vtab fuzzer_vtab;
|
||
+typedef struct fuzzer_cursor fuzzer_cursor;
|
||
+typedef struct fuzzer_rule fuzzer_rule;
|
||
+typedef struct fuzzer_seen fuzzer_seen;
|
||
+typedef struct fuzzer_stem fuzzer_stem;
|
||
+
|
||
+/*
|
||
+** Various types.
|
||
+**
|
||
+** fuzzer_cost is the "cost" of an edit operation.
|
||
+**
|
||
+** fuzzer_len is the length of a matching string.
|
||
+**
|
||
+** fuzzer_ruleid is an ruleset identifier.
|
||
+*/
|
||
+typedef int fuzzer_cost;
|
||
+typedef signed char fuzzer_len;
|
||
+typedef int fuzzer_ruleid;
|
||
+
|
||
+/*
|
||
+** Limits
|
||
+*/
|
||
+#define FUZZER_MX_LENGTH 50 /* Maximum length of a rule string */
|
||
+#define FUZZER_MX_RULEID 2147483647 /* Maximum rule ID */
|
||
+#define FUZZER_MX_COST 1000 /* Maximum single-rule cost */
|
||
+#define FUZZER_MX_OUTPUT_LENGTH 100 /* Maximum length of an output string */
|
||
+
|
||
+
|
||
+/*
|
||
+** Each transformation rule is stored as an instance of this object.
|
||
+** All rules are kept on a linked list sorted by rCost.
|
||
+*/
|
||
+struct fuzzer_rule {
|
||
+ fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
|
||
+ char *zFrom; /* Transform from */
|
||
+ fuzzer_cost rCost; /* Cost of this transformation */
|
||
+ fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */
|
||
+ fuzzer_ruleid iRuleset; /* The rule set to which this rule belongs */
|
||
+ char zTo[4]; /* Transform to (extra space appended) */
|
||
+};
|
||
+
|
||
+/*
|
||
+** A stem object is used to generate variants. It is also used to record
|
||
+** previously generated outputs.
|
||
+**
|
||
+** Every stem is added to a hash table as it is output. Generation of
|
||
+** duplicate stems is suppressed.
|
||
+**
|
||
+** Active stems (those that might generate new outputs) are kepts on a linked
|
||
+** list sorted by increasing cost. The cost is the sum of rBaseCost and
|
||
+** pRule->rCost.
|
||
+*/
|
||
+struct fuzzer_stem {
|
||
+ char *zBasis; /* Word being fuzzed */
|
||
+ const fuzzer_rule *pRule; /* Current rule to apply */
|
||
+ fuzzer_stem *pNext; /* Next stem in rCost order */
|
||
+ fuzzer_stem *pHash; /* Next stem with same hash on zBasis */
|
||
+ fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
|
||
+ fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
|
||
+ fuzzer_len nBasis; /* Length of the zBasis string */
|
||
+ fuzzer_len n; /* Apply pRule at this character offset */
|
||
+};
|
||
+
|
||
+/*
|
||
+** A fuzzer virtual-table object
|
||
+*/
|
||
+struct fuzzer_vtab {
|
||
+ sqlite3_vtab base; /* Base class - must be first */
|
||
+ char *zClassName; /* Name of this class. Default: "fuzzer" */
|
||
+ fuzzer_rule *pRule; /* All active rules in this fuzzer */
|
||
+ int nCursor; /* Number of active cursors */
|
||
+};
|
||
+
|
||
+#define FUZZER_HASH 4001 /* Hash table size */
|
||
+#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */
|
||
+
|
||
+/* A fuzzer cursor object */
|
||
+struct fuzzer_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ sqlite3_int64 iRowid; /* The rowid of the current word */
|
||
+ fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */
|
||
+ fuzzer_cost rLimit; /* Maximum cost of any term */
|
||
+ fuzzer_stem *pStem; /* Stem with smallest rCostX */
|
||
+ fuzzer_stem *pDone; /* Stems already processed to completion */
|
||
+ fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */
|
||
+ int mxQueue; /* Largest used index in aQueue[] */
|
||
+ char *zBuf; /* Temporary use buffer */
|
||
+ int nBuf; /* Bytes allocated for zBuf */
|
||
+ int nStem; /* Number of stems allocated */
|
||
+ int iRuleset; /* Only process rules from this ruleset */
|
||
+ fuzzer_rule nullRule; /* Null rule used first */
|
||
+ fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
|
||
+};
|
||
+
|
||
+/*
|
||
+** The two input rule lists are both sorted in order of increasing
|
||
+** cost. Merge them together into a single list, sorted by cost, and
|
||
+** return a pointer to the head of that list.
|
||
+*/
|
||
+static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
|
||
+ fuzzer_rule head;
|
||
+ fuzzer_rule *pTail;
|
||
+
|
||
+ pTail = &head;
|
||
+ while( pA && pB ){
|
||
+ if( pA->rCost<=pB->rCost ){
|
||
+ pTail->pNext = pA;
|
||
+ pTail = pA;
|
||
+ pA = pA->pNext;
|
||
+ }else{
|
||
+ pTail->pNext = pB;
|
||
+ pTail = pB;
|
||
+ pB = pB->pNext;
|
||
+ }
|
||
+ }
|
||
+ if( pA==0 ){
|
||
+ pTail->pNext = pB;
|
||
+ }else{
|
||
+ pTail->pNext = pA;
|
||
+ }
|
||
+ return head.pNext;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Statement pStmt currently points to a row in the fuzzer data table. This
|
||
+** function allocates and populates a fuzzer_rule structure according to
|
||
+** the content of the row.
|
||
+**
|
||
+** If successful, *ppRule is set to point to the new object and SQLITE_OK
|
||
+** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
|
||
+** to an error message and an SQLite error code returned.
|
||
+*/
|
||
+static int fuzzerLoadOneRule(
|
||
+ fuzzer_vtab *p, /* Fuzzer virtual table handle */
|
||
+ sqlite3_stmt *pStmt, /* Base rule on statements current row */
|
||
+ fuzzer_rule **ppRule, /* OUT: New rule object */
|
||
+ char **pzErr /* OUT: Error message */
|
||
+){
|
||
+ sqlite3_int64 iRuleset = sqlite3_column_int64(pStmt, 0);
|
||
+ const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
|
||
+ const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
|
||
+ int nCost = sqlite3_column_int(pStmt, 3);
|
||
+
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ int nFrom; /* Size of string zFrom, in bytes */
|
||
+ int nTo; /* Size of string zTo, in bytes */
|
||
+ fuzzer_rule *pRule = 0; /* New rule object to return */
|
||
+
|
||
+ if( zFrom==0 ) zFrom = "";
|
||
+ if( zTo==0 ) zTo = "";
|
||
+ nFrom = (int)strlen(zFrom);
|
||
+ nTo = (int)strlen(zTo);
|
||
+
|
||
+ /* Silently ignore null transformations */
|
||
+ if( strcmp(zFrom, zTo)==0 ){
|
||
+ *ppRule = 0;
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+
|
||
+ if( nCost<=0 || nCost>FUZZER_MX_COST ){
|
||
+ *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
|
||
+ p->zClassName, FUZZER_MX_COST
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else
|
||
+ if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
|
||
+ *pzErr = sqlite3_mprintf("%s: maximum string length is %d",
|
||
+ p->zClassName, FUZZER_MX_LENGTH
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else
|
||
+ if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
|
||
+ *pzErr = sqlite3_mprintf("%s: ruleset must be between 0 and %d",
|
||
+ p->zClassName, FUZZER_MX_RULEID
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else{
|
||
+
|
||
+ pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
|
||
+ if( pRule==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ memset(pRule, 0, sizeof(*pRule));
|
||
+ pRule->zFrom = &pRule->zTo[nTo+1];
|
||
+ pRule->nFrom = nFrom;
|
||
+ memcpy(pRule->zFrom, zFrom, nFrom+1);
|
||
+ memcpy(pRule->zTo, zTo, nTo+1);
|
||
+ pRule->nTo = nTo;
|
||
+ pRule->rCost = nCost;
|
||
+ pRule->iRuleset = (int)iRuleset;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ *ppRule = pRule;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Load the content of the fuzzer data table into memory.
|
||
+*/
|
||
+static int fuzzerLoadRules(
|
||
+ sqlite3 *db, /* Database handle */
|
||
+ fuzzer_vtab *p, /* Virtual fuzzer table to configure */
|
||
+ const char *zDb, /* Database containing rules data */
|
||
+ const char *zData, /* Table containing rules data */
|
||
+ char **pzErr /* OUT: Error message */
|
||
+){
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ char *zSql; /* SELECT used to read from rules table */
|
||
+ fuzzer_rule *pHead = 0;
|
||
+
|
||
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
|
||
+ if( zSql==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ int rc2; /* finalize() return code */
|
||
+ sqlite3_stmt *pStmt = 0;
|
||
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
|
||
+ }else if( sqlite3_column_count(pStmt)!=4 ){
|
||
+ *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
|
||
+ p->zClassName, zData, sqlite3_column_count(pStmt)
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else{
|
||
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||
+ fuzzer_rule *pRule = 0;
|
||
+ rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
|
||
+ if( pRule ){
|
||
+ pRule->pNext = pHead;
|
||
+ pHead = pRule;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ rc2 = sqlite3_finalize(pStmt);
|
||
+ if( rc==SQLITE_OK ) rc = rc2;
|
||
+ }
|
||
+ sqlite3_free(zSql);
|
||
+
|
||
+ /* All rules are now in a singly linked list starting at pHead. This
|
||
+ ** block sorts them by cost and then sets fuzzer_vtab.pRule to point to
|
||
+ ** point to the head of the sorted list.
|
||
+ */
|
||
+ if( rc==SQLITE_OK ){
|
||
+ unsigned int i;
|
||
+ fuzzer_rule *pX;
|
||
+ fuzzer_rule *a[15];
|
||
+ for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
|
||
+ while( (pX = pHead)!=0 ){
|
||
+ pHead = pX->pNext;
|
||
+ pX->pNext = 0;
|
||
+ for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
|
||
+ pX = fuzzerMergeRules(a[i], pX);
|
||
+ a[i] = 0;
|
||
+ }
|
||
+ a[i] = fuzzerMergeRules(a[i], pX);
|
||
+ }
|
||
+ for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
|
||
+ pX = fuzzerMergeRules(a[i], pX);
|
||
+ }
|
||
+ p->pRule = fuzzerMergeRules(p->pRule, pX);
|
||
+ }else{
|
||
+ /* An error has occurred. Setting p->pRule to point to the head of the
|
||
+ ** allocated list ensures that the list will be cleaned up in this case.
|
||
+ */
|
||
+ assert( p->pRule==0 );
|
||
+ p->pRule = pHead;
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This function converts an SQL quoted string into an unquoted string
|
||
+** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
||
+** containing the result. The caller should eventually free this buffer
|
||
+** using sqlite3_free.
|
||
+**
|
||
+** Examples:
|
||
+**
|
||
+** "abc" becomes abc
|
||
+** 'xyz' becomes xyz
|
||
+** [pqr] becomes pqr
|
||
+** `mno` becomes mno
|
||
+*/
|
||
+static char *fuzzerDequote(const char *zIn){
|
||
+ int nIn; /* Size of input string, in bytes */
|
||
+ char *zOut; /* Output (dequoted) string */
|
||
+
|
||
+ nIn = (int)strlen(zIn);
|
||
+ zOut = sqlite3_malloc(nIn+1);
|
||
+ if( zOut ){
|
||
+ char q = zIn[0]; /* Quote character (if any ) */
|
||
+
|
||
+ if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
||
+ memcpy(zOut, zIn, nIn+1);
|
||
+ }else{
|
||
+ int iOut = 0; /* Index of next byte to write to output */
|
||
+ int iIn; /* Index of next byte to read from input */
|
||
+
|
||
+ if( q=='[' ) q = ']';
|
||
+ for(iIn=1; iIn<nIn; iIn++){
|
||
+ if( zIn[iIn]==q ) iIn++;
|
||
+ zOut[iOut++] = zIn[iIn];
|
||
+ }
|
||
+ }
|
||
+ assert( (int)strlen(zOut)<=nIn );
|
||
+ }
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+/*
|
||
+** xDisconnect/xDestroy method for the fuzzer module.
|
||
+*/
|
||
+static int fuzzerDisconnect(sqlite3_vtab *pVtab){
|
||
+ fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
|
||
+ assert( p->nCursor==0 );
|
||
+ while( p->pRule ){
|
||
+ fuzzer_rule *pRule = p->pRule;
|
||
+ p->pRule = pRule->pNext;
|
||
+ sqlite3_free(pRule);
|
||
+ }
|
||
+ sqlite3_free(p);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** xConnect/xCreate method for the fuzzer module. Arguments are:
|
||
+**
|
||
+** argv[0] -> module name ("fuzzer")
|
||
+** argv[1] -> database name
|
||
+** argv[2] -> table name
|
||
+** argv[3] -> fuzzer rule table name
|
||
+*/
|
||
+static int fuzzerConnect(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ int rc = SQLITE_OK; /* Return code */
|
||
+ fuzzer_vtab *pNew = 0; /* New virtual table */
|
||
+ const char *zModule = argv[0];
|
||
+ const char *zDb = argv[1];
|
||
+
|
||
+ if( argc!=4 ){
|
||
+ *pzErr = sqlite3_mprintf(
|
||
+ "%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
|
||
+ );
|
||
+ rc = SQLITE_ERROR;
|
||
+ }else{
|
||
+ int nModule; /* Length of zModule, in bytes */
|
||
+
|
||
+ nModule = (int)strlen(zModule);
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1);
|
||
+ if( pNew==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ char *zTab; /* Dequoted name of fuzzer data table */
|
||
+
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->zClassName = (char*)&pNew[1];
|
||
+ memcpy(pNew->zClassName, zModule, nModule+1);
|
||
+
|
||
+ zTab = fuzzerDequote(argv[3]);
|
||
+ if( zTab==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ rc = fuzzerLoadRules(db, pNew, zDb, zTab, pzErr);
|
||
+ sqlite3_free(zTab);
|
||
+ }
|
||
+
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,ruleset)");
|
||
+ }
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ fuzzerDisconnect((sqlite3_vtab *)pNew);
|
||
+ pNew = 0;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ *ppVtab = (sqlite3_vtab *)pNew;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open a new fuzzer cursor.
|
||
+*/
|
||
+static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||
+ fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
|
||
+ fuzzer_cursor *pCur;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ pCur->pVtab = p;
|
||
+ *ppCursor = &pCur->base;
|
||
+ p->nCursor++;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Free all stems in a list.
|
||
+*/
|
||
+static void fuzzerClearStemList(fuzzer_stem *pStem){
|
||
+ while( pStem ){
|
||
+ fuzzer_stem *pNext = pStem->pNext;
|
||
+ sqlite3_free(pStem);
|
||
+ pStem = pNext;
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
||
+** to indicate that it is at EOF.
|
||
+*/
|
||
+static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){
|
||
+ int i;
|
||
+ fuzzerClearStemList(pCur->pStem);
|
||
+ fuzzerClearStemList(pCur->pDone);
|
||
+ for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]);
|
||
+ pCur->rLimit = (fuzzer_cost)0;
|
||
+ if( clearHash && pCur->nStem ){
|
||
+ pCur->mxQueue = 0;
|
||
+ pCur->pStem = 0;
|
||
+ pCur->pDone = 0;
|
||
+ memset(pCur->aQueue, 0, sizeof(pCur->aQueue));
|
||
+ memset(pCur->apHash, 0, sizeof(pCur->apHash));
|
||
+ }
|
||
+ pCur->nStem = 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Close a fuzzer cursor.
|
||
+*/
|
||
+static int fuzzerClose(sqlite3_vtab_cursor *cur){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor *)cur;
|
||
+ fuzzerClearCursor(pCur, 0);
|
||
+ sqlite3_free(pCur->zBuf);
|
||
+ pCur->pVtab->nCursor--;
|
||
+ sqlite3_free(pCur);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compute the current output term for a fuzzer_stem.
|
||
+*/
|
||
+static int fuzzerRender(
|
||
+ fuzzer_stem *pStem, /* The stem to be rendered */
|
||
+ char **pzBuf, /* Write results into this buffer. realloc if needed */
|
||
+ int *pnBuf /* Size of the buffer */
|
||
+){
|
||
+ const fuzzer_rule *pRule = pStem->pRule;
|
||
+ int n; /* Size of output term without nul-term */
|
||
+ char *z; /* Buffer to assemble output term in */
|
||
+
|
||
+ n = pStem->nBasis + pRule->nTo - pRule->nFrom;
|
||
+ if( (*pnBuf)<n+1 ){
|
||
+ (*pzBuf) = sqlite3_realloc((*pzBuf), n+100);
|
||
+ if( (*pzBuf)==0 ) return SQLITE_NOMEM;
|
||
+ (*pnBuf) = n+100;
|
||
+ }
|
||
+ n = pStem->n;
|
||
+ z = *pzBuf;
|
||
+ if( n<0 ){
|
||
+ memcpy(z, pStem->zBasis, pStem->nBasis+1);
|
||
+ }else{
|
||
+ memcpy(z, pStem->zBasis, n);
|
||
+ memcpy(&z[n], pRule->zTo, pRule->nTo);
|
||
+ memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
|
||
+ pStem->nBasis-n-pRule->nFrom+1);
|
||
+ }
|
||
+
|
||
+ assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 );
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compute a hash on zBasis.
|
||
+*/
|
||
+static unsigned int fuzzerHash(const char *z){
|
||
+ unsigned int h = 0;
|
||
+ while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); }
|
||
+ return h % FUZZER_HASH;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Current cost of a stem
|
||
+*/
|
||
+static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){
|
||
+ return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost;
|
||
+}
|
||
+
|
||
+#if 0
|
||
+/*
|
||
+** Print a description of a fuzzer_stem on stderr.
|
||
+*/
|
||
+static void fuzzerStemPrint(
|
||
+ const char *zPrefix,
|
||
+ fuzzer_stem *pStem,
|
||
+ const char *zSuffix
|
||
+){
|
||
+ if( pStem->n<0 ){
|
||
+ fprintf(stderr, "%s[%s](%d)-->self%s",
|
||
+ zPrefix,
|
||
+ pStem->zBasis, pStem->rBaseCost,
|
||
+ zSuffix
|
||
+ );
|
||
+ }else{
|
||
+ char *zBuf = 0;
|
||
+ int nBuf = 0;
|
||
+ if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return;
|
||
+ fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s",
|
||
+ zPrefix,
|
||
+ pStem->zBasis, pStem->rBaseCost, zBuf, pStem->,
|
||
+ zSuffix
|
||
+ );
|
||
+ sqlite3_free(zBuf);
|
||
+ }
|
||
+}
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Return 1 if the string to which the cursor is point has already
|
||
+** been emitted. Return 0 if not. Return -1 on a memory allocation
|
||
+** failures.
|
||
+*/
|
||
+static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
||
+ unsigned int h;
|
||
+ fuzzer_stem *pLookup;
|
||
+
|
||
+ if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
|
||
+ return -1;
|
||
+ }
|
||
+ h = fuzzerHash(pCur->zBuf);
|
||
+ pLookup = pCur->apHash[h];
|
||
+ while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
|
||
+ pLookup = pLookup->pHash;
|
||
+ }
|
||
+ return pLookup!=0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** If argument pRule is NULL, this function returns false.
|
||
+**
|
||
+** Otherwise, it returns true if rule pRule should be skipped. A rule
|
||
+** should be skipped if it does not belong to rule-set iRuleset, or if
|
||
+** applying it to stem pStem would create a string longer than
|
||
+** FUZZER_MX_OUTPUT_LENGTH bytes.
|
||
+*/
|
||
+static int fuzzerSkipRule(
|
||
+ const fuzzer_rule *pRule, /* Determine whether or not to skip this */
|
||
+ fuzzer_stem *pStem, /* Stem rule may be applied to */
|
||
+ int iRuleset /* Rule-set used by the current query */
|
||
+){
|
||
+ return pRule && (
|
||
+ (pRule->iRuleset!=iRuleset)
|
||
+ || (pStem->nBasis + pRule->nTo - pRule->nFrom)>FUZZER_MX_OUTPUT_LENGTH
|
||
+ );
|
||
+}
|
||
+
|
||
+/*
|
||
+** Advance a fuzzer_stem to its next value. Return 0 if there are
|
||
+** no more values that can be generated by this fuzzer_stem. Return
|
||
+** -1 on a memory allocation failure.
|
||
+*/
|
||
+static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
||
+ const fuzzer_rule *pRule;
|
||
+ while( (pRule = pStem->pRule)!=0 ){
|
||
+ assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
|
||
+ while( pStem->n < pStem->nBasis - pRule->nFrom ){
|
||
+ pStem->n++;
|
||
+ if( pRule->nFrom==0
|
||
+ || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0
|
||
+ ){
|
||
+ /* Found a rewrite case. Make sure it is not a duplicate */
|
||
+ int rc = fuzzerSeen(pCur, pStem);
|
||
+ if( rc<0 ) return -1;
|
||
+ if( rc==0 ){
|
||
+ fuzzerCost(pStem);
|
||
+ return 1;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ pStem->n = -1;
|
||
+ do{
|
||
+ pRule = pRule->pNext;
|
||
+ }while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) );
|
||
+ pStem->pRule = pRule;
|
||
+ if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The two input stem lists are both sorted in order of increasing
|
||
+** rCostX. Merge them together into a single list, sorted by rCostX, and
|
||
+** return a pointer to the head of that new list.
|
||
+*/
|
||
+static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){
|
||
+ fuzzer_stem head;
|
||
+ fuzzer_stem *pTail;
|
||
+
|
||
+ pTail = &head;
|
||
+ while( pA && pB ){
|
||
+ if( pA->rCostX<=pB->rCostX ){
|
||
+ pTail->pNext = pA;
|
||
+ pTail = pA;
|
||
+ pA = pA->pNext;
|
||
+ }else{
|
||
+ pTail->pNext = pB;
|
||
+ pTail = pB;
|
||
+ pB = pB->pNext;
|
||
+ }
|
||
+ }
|
||
+ if( pA==0 ){
|
||
+ pTail->pNext = pB;
|
||
+ }else{
|
||
+ pTail->pNext = pA;
|
||
+ }
|
||
+ return head.pNext;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Load pCur->pStem with the lowest-cost stem. Return a pointer
|
||
+** to the lowest-cost stem.
|
||
+*/
|
||
+static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){
|
||
+ fuzzer_stem *pBest, *pX;
|
||
+ int iBest;
|
||
+ int i;
|
||
+
|
||
+ if( pCur->pStem==0 ){
|
||
+ iBest = -1;
|
||
+ pBest = 0;
|
||
+ for(i=0; i<=pCur->mxQueue; i++){
|
||
+ pX = pCur->aQueue[i];
|
||
+ if( pX==0 ) continue;
|
||
+ if( pBest==0 || pBest->rCostX>pX->rCostX ){
|
||
+ pBest = pX;
|
||
+ iBest = i;
|
||
+ }
|
||
+ }
|
||
+ if( pBest ){
|
||
+ pCur->aQueue[iBest] = pBest->pNext;
|
||
+ pBest->pNext = 0;
|
||
+ pCur->pStem = pBest;
|
||
+ }
|
||
+ }
|
||
+ return pCur->pStem;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Insert pNew into queue of pending stems. Then find the stem
|
||
+** with the lowest rCostX and move it into pCur->pStem.
|
||
+** list. The insert is done such the pNew is in the correct order
|
||
+** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost.
|
||
+*/
|
||
+static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){
|
||
+ fuzzer_stem *pX;
|
||
+ int i;
|
||
+
|
||
+ /* If pCur->pStem exists and is greater than pNew, then make pNew
|
||
+ ** the new pCur->pStem and insert the old pCur->pStem instead.
|
||
+ */
|
||
+ if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){
|
||
+ pNew->pNext = 0;
|
||
+ pCur->pStem = pNew;
|
||
+ pNew = pX;
|
||
+ }
|
||
+
|
||
+ /* Insert the new value */
|
||
+ pNew->pNext = 0;
|
||
+ pX = pNew;
|
||
+ for(i=0; i<=pCur->mxQueue; i++){
|
||
+ if( pCur->aQueue[i] ){
|
||
+ pX = fuzzerMergeStems(pX, pCur->aQueue[i]);
|
||
+ pCur->aQueue[i] = 0;
|
||
+ }else{
|
||
+ pCur->aQueue[i] = pX;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if( i>pCur->mxQueue ){
|
||
+ if( i<FUZZER_NQUEUE ){
|
||
+ pCur->mxQueue = i;
|
||
+ pCur->aQueue[i] = pX;
|
||
+ }else{
|
||
+ assert( pCur->mxQueue==FUZZER_NQUEUE-1 );
|
||
+ pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]);
|
||
+ pCur->aQueue[FUZZER_NQUEUE-1] = pX;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return fuzzerLowestCostStem(pCur);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Allocate a new fuzzer_stem. Add it to the hash table but do not
|
||
+** link it into either the pCur->pStem or pCur->pDone lists.
|
||
+*/
|
||
+static fuzzer_stem *fuzzerNewStem(
|
||
+ fuzzer_cursor *pCur,
|
||
+ const char *zWord,
|
||
+ fuzzer_cost rBaseCost
|
||
+){
|
||
+ fuzzer_stem *pNew;
|
||
+ fuzzer_rule *pRule;
|
||
+ unsigned int h;
|
||
+
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) + (int)strlen(zWord) + 1 );
|
||
+ if( pNew==0 ) return 0;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->zBasis = (char*)&pNew[1];
|
||
+ pNew->nBasis = (int)strlen(zWord);
|
||
+ memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
|
||
+ pRule = pCur->pVtab->pRule;
|
||
+ while( fuzzerSkipRule(pRule, pNew, pCur->iRuleset) ){
|
||
+ pRule = pRule->pNext;
|
||
+ }
|
||
+ pNew->pRule = pRule;
|
||
+ pNew->n = -1;
|
||
+ pNew->rBaseCost = pNew->rCostX = rBaseCost;
|
||
+ h = fuzzerHash(pNew->zBasis);
|
||
+ pNew->pHash = pCur->apHash[h];
|
||
+ pCur->apHash[h] = pNew;
|
||
+ pCur->nStem++;
|
||
+ return pNew;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Advance a cursor to its next row of output
|
||
+*/
|
||
+static int fuzzerNext(sqlite3_vtab_cursor *cur){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
||
+ int rc;
|
||
+ fuzzer_stem *pStem, *pNew;
|
||
+
|
||
+ pCur->iRowid++;
|
||
+
|
||
+ /* Use the element the cursor is currently point to to create
|
||
+ ** a new stem and insert the new stem into the priority queue.
|
||
+ */
|
||
+ pStem = pCur->pStem;
|
||
+ if( pStem->rCostX>0 ){
|
||
+ rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf);
|
||
+ if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
|
||
+ pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX);
|
||
+ if( pNew ){
|
||
+ if( fuzzerAdvance(pCur, pNew)==0 ){
|
||
+ pNew->pNext = pCur->pDone;
|
||
+ pCur->pDone = pNew;
|
||
+ }else{
|
||
+ if( fuzzerInsert(pCur, pNew)==pNew ){
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ }
|
||
+ }else{
|
||
+ return SQLITE_NOMEM;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Adjust the priority queue so that the first element of the
|
||
+ ** stem list is the next lowest cost word.
|
||
+ */
|
||
+ while( (pStem = pCur->pStem)!=0 ){
|
||
+ int res = fuzzerAdvance(pCur, pStem);
|
||
+ if( res<0 ){
|
||
+ return SQLITE_NOMEM;
|
||
+ }else if( res>0 ){
|
||
+ pCur->pStem = 0;
|
||
+ pStem = fuzzerInsert(pCur, pStem);
|
||
+ if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
|
||
+ if( rc<0 ) return SQLITE_NOMEM;
|
||
+ continue;
|
||
+ }
|
||
+ return SQLITE_OK; /* New word found */
|
||
+ }
|
||
+ pCur->pStem = 0;
|
||
+ pStem->pNext = pCur->pDone;
|
||
+ pCur->pDone = pStem;
|
||
+ if( fuzzerLowestCostStem(pCur) ){
|
||
+ rc = fuzzerSeen(pCur, pCur->pStem);
|
||
+ if( rc<0 ) return SQLITE_NOMEM;
|
||
+ if( rc==0 ){
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Reach this point only if queue has been exhausted and there is
|
||
+ ** nothing left to be output. */
|
||
+ pCur->rLimit = (fuzzer_cost)0;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Called to "rewind" a cursor back to the beginning so that
|
||
+** it starts its output over again. Always called at least once
|
||
+** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call.
|
||
+*/
|
||
+static int fuzzerFilter(
|
||
+ sqlite3_vtab_cursor *pVtabCursor,
|
||
+ int idxNum, const char *idxStr,
|
||
+ int argc, sqlite3_value **argv
|
||
+){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
|
||
+ const char *zWord = "";
|
||
+ fuzzer_stem *pStem;
|
||
+ int idx;
|
||
+
|
||
+ fuzzerClearCursor(pCur, 1);
|
||
+ pCur->rLimit = 2147483647;
|
||
+ idx = 0;
|
||
+ if( idxNum & 1 ){
|
||
+ zWord = (const char*)sqlite3_value_text(argv[0]);
|
||
+ idx++;
|
||
+ }
|
||
+ if( idxNum & 2 ){
|
||
+ pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]);
|
||
+ idx++;
|
||
+ }
|
||
+ if( idxNum & 4 ){
|
||
+ pCur->iRuleset = (fuzzer_cost)sqlite3_value_int(argv[idx]);
|
||
+ idx++;
|
||
+ }
|
||
+ pCur->nullRule.pNext = pCur->pVtab->pRule;
|
||
+ pCur->nullRule.rCost = 0;
|
||
+ pCur->nullRule.nFrom = 0;
|
||
+ pCur->nullRule.nTo = 0;
|
||
+ pCur->nullRule.zFrom = "";
|
||
+ pCur->iRowid = 1;
|
||
+ assert( pCur->pStem==0 );
|
||
+
|
||
+ /* If the query term is longer than FUZZER_MX_OUTPUT_LENGTH bytes, this
|
||
+ ** query will return zero rows. */
|
||
+ if( (int)strlen(zWord)<FUZZER_MX_OUTPUT_LENGTH ){
|
||
+ pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
|
||
+ if( pStem==0 ) return SQLITE_NOMEM;
|
||
+ pStem->pRule = &pCur->nullRule;
|
||
+ pStem->n = pStem->nBasis;
|
||
+ }else{
|
||
+ pCur->rLimit = 0;
|
||
+ }
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Only the word and distance columns have values. All other columns
|
||
+** return NULL
|
||
+*/
|
||
+static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
||
+ if( i==0 ){
|
||
+ /* the "word" column */
|
||
+ if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){
|
||
+ return SQLITE_NOMEM;
|
||
+ }
|
||
+ sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
|
||
+ }else if( i==1 ){
|
||
+ /* the "distance" column */
|
||
+ sqlite3_result_int(ctx, pCur->pStem->rCostX);
|
||
+ }else{
|
||
+ /* All other columns are NULL */
|
||
+ sqlite3_result_null(ctx);
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The rowid.
|
||
+*/
|
||
+static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
||
+ *pRowid = pCur->iRowid;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal
|
||
+** that the cursor has nothing more to output.
|
||
+*/
|
||
+static int fuzzerEof(sqlite3_vtab_cursor *cur){
|
||
+ fuzzer_cursor *pCur = (fuzzer_cursor*)cur;
|
||
+ return pCur->rLimit<=(fuzzer_cost)0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Search for terms of these forms:
|
||
+**
|
||
+** (A) word MATCH $str
|
||
+** (B1) distance < $value
|
||
+** (B2) distance <= $value
|
||
+** (C) ruleid == $ruleid
|
||
+**
|
||
+** The distance< and distance<= are both treated as distance<=.
|
||
+** The query plan number is a bit vector:
|
||
+**
|
||
+** bit 1: Term of the form (A) found
|
||
+** bit 2: Term like (B1) or (B2) found
|
||
+** bit 3: Term like (C) found
|
||
+**
|
||
+** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
|
||
+** then $value is in filter.argv[0] if bit-1 is clear and is in
|
||
+** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
|
||
+** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
|
||
+** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
|
||
+** filter.argv[2] if both bit-1 and bit-2 are set.
|
||
+*/
|
||
+static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||
+ int iPlan = 0;
|
||
+ int iDistTerm = -1;
|
||
+ int iRulesetTerm = -1;
|
||
+ int i;
|
||
+ int seenMatch = 0;
|
||
+ const struct sqlite3_index_constraint *pConstraint;
|
||
+ double rCost = 1e12;
|
||
+
|
||
+ pConstraint = pIdxInfo->aConstraint;
|
||
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||
+ if( pConstraint->iColumn==0
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||
+ seenMatch = 1;
|
||
+ }
|
||
+ if( pConstraint->usable==0 ) continue;
|
||
+ if( (iPlan & 1)==0
|
||
+ && pConstraint->iColumn==0
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
||
+ ){
|
||
+ iPlan |= 1;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ rCost /= 1e6;
|
||
+ }
|
||
+ if( (iPlan & 2)==0
|
||
+ && pConstraint->iColumn==1
|
||
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
||
+ ){
|
||
+ iPlan |= 2;
|
||
+ iDistTerm = i;
|
||
+ rCost /= 10.0;
|
||
+ }
|
||
+ if( (iPlan & 4)==0
|
||
+ && pConstraint->iColumn==2
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 4;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ iRulesetTerm = i;
|
||
+ rCost /= 10.0;
|
||
+ }
|
||
+ }
|
||
+ if( iPlan & 2 ){
|
||
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
|
||
+ }
|
||
+ if( iPlan & 4 ){
|
||
+ int idx = 1;
|
||
+ if( iPlan & 1 ) idx++;
|
||
+ if( iPlan & 2 ) idx++;
|
||
+ pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx;
|
||
+ }
|
||
+ pIdxInfo->idxNum = iPlan;
|
||
+ if( pIdxInfo->nOrderBy==1
|
||
+ && pIdxInfo->aOrderBy[0].iColumn==1
|
||
+ && pIdxInfo->aOrderBy[0].desc==0
|
||
+ ){
|
||
+ pIdxInfo->orderByConsumed = 1;
|
||
+ }
|
||
+ if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
|
||
+ pIdxInfo->estimatedCost = rCost;
|
||
+
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** A virtual table module that implements the "fuzzer".
|
||
+*/
|
||
+static sqlite3_module fuzzerModule = {
|
||
+ 0, /* iVersion */
|
||
+ fuzzerConnect,
|
||
+ fuzzerConnect,
|
||
+ fuzzerBestIndex,
|
||
+ fuzzerDisconnect,
|
||
+ fuzzerDisconnect,
|
||
+ fuzzerOpen, /* xOpen - open a cursor */
|
||
+ fuzzerClose, /* xClose - close a cursor */
|
||
+ fuzzerFilter, /* xFilter - configure scan constraints */
|
||
+ fuzzerNext, /* xNext - advance a cursor */
|
||
+ fuzzerEof, /* xEof - check for end of scan */
|
||
+ fuzzerColumn, /* xColumn - read data */
|
||
+ fuzzerRowid, /* xRowid - read data */
|
||
+ 0, /* xUpdate */
|
||
+ 0, /* xBegin */
|
||
+ 0, /* xSync */
|
||
+ 0, /* xCommit */
|
||
+ 0, /* xRollback */
|
||
+ 0, /* xFindMethod */
|
||
+ 0, /* xRename */
|
||
+};
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_fuzzer_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
|
||
+#endif
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0);
|
||
+#endif
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/ieee754.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/ieee754.c 2015-01-31 00:31:56.406143700 +0100
|
||
@@ -0,0 +1,149 @@
|
||
+/*
|
||
+** 2013-04-17
|
||
+**
|
||
+** 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 SQLite extension implements functions for the exact display
|
||
+** and input of IEEE754 Binary64 floating-point numbers.
|
||
+**
|
||
+** ieee754(X)
|
||
+** ieee754(Y,Z)
|
||
+**
|
||
+** In the first form, the value X should be a floating-point number.
|
||
+** The function will return a string of the form 'ieee754(Y,Z)' where
|
||
+** Y and Z are integers such that X==Y*pow(2,Z).
|
||
+**
|
||
+** In the second form, Y and Z are integers which are the mantissa and
|
||
+** base-2 exponent of a new floating point number. The function returns
|
||
+** a floating-point value equal to Y*pow(2,Z).
|
||
+**
|
||
+** Examples:
|
||
+**
|
||
+** ieee754(2.0) -> 'ieee754(2,0)'
|
||
+** ieee754(45.25) -> 'ieee754(181,-2)'
|
||
+** ieee754(2, 0) -> 2.0
|
||
+** ieee754(181, -2) -> 45.25
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+
|
||
+/*
|
||
+** Implementation of the ieee754() function
|
||
+*/
|
||
+static void ieee754func(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ if( argc==1 ){
|
||
+ sqlite3_int64 m, a;
|
||
+ double r;
|
||
+ int e;
|
||
+ int isNeg;
|
||
+ char zResult[100];
|
||
+ assert( sizeof(m)==sizeof(r) );
|
||
+ if( sqlite3_value_type(argv[0])!=SQLITE_FLOAT ) return;
|
||
+ r = sqlite3_value_double(argv[0]);
|
||
+ if( r<0.0 ){
|
||
+ isNeg = 1;
|
||
+ r = -r;
|
||
+ }else{
|
||
+ isNeg = 0;
|
||
+ }
|
||
+ memcpy(&a,&r,sizeof(a));
|
||
+ if( a==0 ){
|
||
+ e = 0;
|
||
+ m = 0;
|
||
+ }else{
|
||
+ e = a>>52;
|
||
+ m = a & ((((sqlite3_int64)1)<<52)-1);
|
||
+ m |= ((sqlite3_int64)1)<<52;
|
||
+ while( e<1075 && m>0 && (m&1)==0 ){
|
||
+ m >>= 1;
|
||
+ e++;
|
||
+ }
|
||
+ if( isNeg ) m = -m;
|
||
+ }
|
||
+ sqlite3_snprintf(sizeof(zResult), zResult, "ieee754(%lld,%d)",
|
||
+ m, e-1075);
|
||
+ sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
|
||
+ }else if( argc==2 ){
|
||
+ sqlite3_int64 m, e, a;
|
||
+ double r;
|
||
+ int isNeg = 0;
|
||
+ m = sqlite3_value_int64(argv[0]);
|
||
+ e = sqlite3_value_int64(argv[1]);
|
||
+ if( m<0 ){
|
||
+ isNeg = 1;
|
||
+ m = -m;
|
||
+ if( m<0 ) return;
|
||
+ }else if( m==0 && e>1000 && e<1000 ){
|
||
+ sqlite3_result_double(context, 0.0);
|
||
+ return;
|
||
+ }
|
||
+ while( (m>>32)&0xffe00000 ){
|
||
+ m >>= 1;
|
||
+ e++;
|
||
+ }
|
||
+ while( ((m>>32)&0xfff00000)==0 ){
|
||
+ m <<= 1;
|
||
+ e--;
|
||
+ }
|
||
+ e += 1075;
|
||
+ if( e<0 ) e = m = 0;
|
||
+ if( e>0x7ff ) m = 0;
|
||
+ a = m & ((((sqlite3_int64)1)<<52)-1);
|
||
+ a |= e<<52;
|
||
+ if( isNeg ) a |= ((sqlite3_int64)1)<<63;
|
||
+ memcpy(&r, &a, sizeof(r));
|
||
+ sqlite3_result_double(context, r);
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_ieee_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
||
+ ieee754func, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
||
+ ieee754func, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "ieee754", 1, SQLITE_UTF8, 0,
|
||
+ ieee754func, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "ieee754", 2, SQLITE_UTF8, 0,
|
||
+ ieee754func, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/nextchar.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/nextchar.c 2015-01-31 00:31:56.414144200 +0100
|
||
@@ -0,0 +1,333 @@
|
||
+/*
|
||
+** 2013-02-28
|
||
+**
|
||
+** 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 contains code to implement the next_char(A,T,F,W,C) SQL function.
|
||
+**
|
||
+** The next_char(A,T,F,W,C) function finds all valid "next" characters for
|
||
+** string A given the vocabulary in T.F. If the W value exists and is a
|
||
+** non-empty string, then it is an SQL expression that limits the entries
|
||
+** in T.F that will be considered. If C exists and is a non-empty string,
|
||
+** then it is the name of the collating sequence to use for comparison. If
|
||
+**
|
||
+** Only the first three arguments are required. If the C parameter is
|
||
+** omitted or is NULL or is an empty string, then the default collating
|
||
+** sequence of T.F is used for comparision. If the W parameter is omitted
|
||
+** or is NULL or is an empty string, then no filtering of the output is
|
||
+** done.
|
||
+**
|
||
+** The T.F column should be indexed using collation C or else this routine
|
||
+** will be quite slow.
|
||
+**
|
||
+** For example, suppose an application has a dictionary like this:
|
||
+**
|
||
+** CREATE TABLE dictionary(word TEXT UNIQUE);
|
||
+**
|
||
+** Further suppose that for user keypad entry, it is desired to disable
|
||
+** (gray out) keys that are not valid as the next character. If the
|
||
+** the user has previously entered (say) 'cha' then to find all allowed
|
||
+** next characters (and thereby determine when keys should not be grayed
|
||
+** out) run the following query:
|
||
+**
|
||
+** SELECT next_char('cha','dictionary','word');
|
||
+**
|
||
+** IMPLEMENTATION NOTES:
|
||
+**
|
||
+** The next_char function is implemented using recursive SQL that makes
|
||
+** use of the table name and column name as part of a query. If either
|
||
+** the table name or column name are keywords or contain special characters,
|
||
+** then they should be escaped. For example:
|
||
+**
|
||
+** SELECT next_char('cha','[dictionary]','[word]');
|
||
+**
|
||
+** This also means that the table name can be a subquery:
|
||
+**
|
||
+** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <string.h>
|
||
+
|
||
+/*
|
||
+** A structure to hold context of the next_char() computation across
|
||
+** nested function calls.
|
||
+*/
|
||
+typedef struct nextCharContext nextCharContext;
|
||
+struct nextCharContext {
|
||
+ sqlite3 *db; /* Database connection */
|
||
+ sqlite3_stmt *pStmt; /* Prepared statement used to query */
|
||
+ const unsigned char *zPrefix; /* Prefix to scan */
|
||
+ int nPrefix; /* Size of zPrefix in bytes */
|
||
+ int nAlloc; /* Space allocated to aResult */
|
||
+ int nUsed; /* Space used in aResult */
|
||
+ unsigned int *aResult; /* Array of next characters */
|
||
+ int mallocFailed; /* True if malloc fails */
|
||
+ int otherError; /* True for any other failure */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Append a result character if the character is not already in the
|
||
+** result.
|
||
+*/
|
||
+static void nextCharAppend(nextCharContext *p, unsigned c){
|
||
+ int i;
|
||
+ for(i=0; i<p->nUsed; i++){
|
||
+ if( p->aResult[i]==c ) return;
|
||
+ }
|
||
+ if( p->nUsed+1 > p->nAlloc ){
|
||
+ unsigned int *aNew;
|
||
+ int n = p->nAlloc*2 + 30;
|
||
+ aNew = sqlite3_realloc(p->aResult, n*sizeof(unsigned int));
|
||
+ if( aNew==0 ){
|
||
+ p->mallocFailed = 1;
|
||
+ return;
|
||
+ }else{
|
||
+ p->aResult = aNew;
|
||
+ p->nAlloc = n;
|
||
+ }
|
||
+ }
|
||
+ p->aResult[p->nUsed++] = c;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Write a character into z[] as UTF8. Return the number of bytes needed
|
||
+** to hold the character
|
||
+*/
|
||
+static int writeUtf8(unsigned char *z, unsigned c){
|
||
+ if( c<0x00080 ){
|
||
+ z[0] = (unsigned char)(c&0xff);
|
||
+ return 1;
|
||
+ }
|
||
+ if( c<0x00800 ){
|
||
+ z[0] = 0xC0 + (unsigned char)((c>>6)&0x1F);
|
||
+ z[1] = 0x80 + (unsigned char)(c & 0x3F);
|
||
+ return 2;
|
||
+ }
|
||
+ if( c<0x10000 ){
|
||
+ z[0] = 0xE0 + (unsigned char)((c>>12)&0x0F);
|
||
+ z[1] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||
+ z[2] = 0x80 + (unsigned char)(c & 0x3F);
|
||
+ return 3;
|
||
+ }
|
||
+ z[0] = 0xF0 + (unsigned char)((c>>18) & 0x07);
|
||
+ z[1] = 0x80 + (unsigned char)((c>>12) & 0x3F);
|
||
+ z[2] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||
+ z[3] = 0x80 + (unsigned char)(c & 0x3F);
|
||
+ return 4;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Read a UTF8 character out of z[] and write it into *pOut. Return
|
||
+** the number of bytes in z[] that were used to construct the character.
|
||
+*/
|
||
+static int readUtf8(const unsigned char *z, unsigned *pOut){
|
||
+ static const unsigned char validBits[] = {
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||
+ };
|
||
+ unsigned c = z[0];
|
||
+ if( c<0xc0 ){
|
||
+ *pOut = c;
|
||
+ return 1;
|
||
+ }else{
|
||
+ int n = 1;
|
||
+ c = validBits[c-0xc0];
|
||
+ while( (z[n] & 0xc0)==0x80 ){
|
||
+ c = (c<<6) + (0x3f & z[n++]);
|
||
+ }
|
||
+ if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){
|
||
+ c = 0xFFFD;
|
||
+ }
|
||
+ *pOut = c;
|
||
+ return n;
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** The nextCharContext structure has been set up. Add all "next" characters
|
||
+** to the result set.
|
||
+*/
|
||
+static void findNextChars(nextCharContext *p){
|
||
+ unsigned cPrev = 0;
|
||
+ unsigned char zPrev[8];
|
||
+ int n, rc;
|
||
+
|
||
+ for(;;){
|
||
+ sqlite3_bind_text(p->pStmt, 1, (char*)p->zPrefix, p->nPrefix,
|
||
+ SQLITE_STATIC);
|
||
+ n = writeUtf8(zPrev, cPrev+1);
|
||
+ sqlite3_bind_text(p->pStmt, 2, (char*)zPrev, n, SQLITE_STATIC);
|
||
+ rc = sqlite3_step(p->pStmt);
|
||
+ if( rc==SQLITE_DONE ){
|
||
+ sqlite3_reset(p->pStmt);
|
||
+ return;
|
||
+ }else if( rc!=SQLITE_ROW ){
|
||
+ p->otherError = rc;
|
||
+ return;
|
||
+ }else{
|
||
+ const unsigned char *zOut = sqlite3_column_text(p->pStmt, 0);
|
||
+ unsigned cNext;
|
||
+ n = readUtf8(zOut+p->nPrefix, &cNext);
|
||
+ sqlite3_reset(p->pStmt);
|
||
+ nextCharAppend(p, cNext);
|
||
+ cPrev = cNext;
|
||
+ if( p->mallocFailed ) return;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** next_character(A,T,F,W)
|
||
+**
|
||
+** Return a string composted of all next possible characters after
|
||
+** A for elements of T.F. If W is supplied, then it is an SQL expression
|
||
+** that limits the elements in T.F that are considered.
|
||
+*/
|
||
+static void nextCharFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ nextCharContext c;
|
||
+ const unsigned char *zTable = sqlite3_value_text(argv[1]);
|
||
+ const unsigned char *zField = sqlite3_value_text(argv[2]);
|
||
+ const unsigned char *zWhere;
|
||
+ const unsigned char *zCollName;
|
||
+ char *zWhereClause = 0;
|
||
+ char *zColl = 0;
|
||
+ char *zSql;
|
||
+ int rc;
|
||
+
|
||
+ memset(&c, 0, sizeof(c));
|
||
+ c.db = sqlite3_context_db_handle(context);
|
||
+ c.zPrefix = sqlite3_value_text(argv[0]);
|
||
+ c.nPrefix = sqlite3_value_bytes(argv[0]);
|
||
+ if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
|
||
+ if( argc>=4
|
||
+ && (zWhere = sqlite3_value_text(argv[3]))!=0
|
||
+ && zWhere[0]!=0
|
||
+ ){
|
||
+ zWhereClause = sqlite3_mprintf("AND (%s)", zWhere);
|
||
+ if( zWhereClause==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ return;
|
||
+ }
|
||
+ }else{
|
||
+ zWhereClause = "";
|
||
+ }
|
||
+ if( argc>=5
|
||
+ && (zCollName = sqlite3_value_text(argv[4]))!=0
|
||
+ && zCollName[0]!=0
|
||
+ ){
|
||
+ zColl = sqlite3_mprintf("collate \"%w\"", zCollName);
|
||
+ if( zColl==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
||
+ return;
|
||
+ }
|
||
+ }else{
|
||
+ zColl = "";
|
||
+ }
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT %s FROM %s"
|
||
+ " WHERE %s>=(?1 || ?2) %s"
|
||
+ " AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
|
||
+ " %s"
|
||
+ " ORDER BY 1 %s ASC LIMIT 1",
|
||
+ zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
|
||
+ );
|
||
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
||
+ if( zColl[0] ) sqlite3_free(zColl);
|
||
+ if( zSql==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ rc = sqlite3_prepare_v2(c.db, zSql, -1, &c.pStmt, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ if( rc ){
|
||
+ sqlite3_result_error(context, sqlite3_errmsg(c.db), -1);
|
||
+ return;
|
||
+ }
|
||
+ findNextChars(&c);
|
||
+ if( c.mallocFailed ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else{
|
||
+ unsigned char *pRes;
|
||
+ pRes = sqlite3_malloc( c.nUsed*4 + 1 );
|
||
+ if( pRes==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else{
|
||
+ int i;
|
||
+ int n = 0;
|
||
+ for(i=0; i<c.nUsed; i++){
|
||
+ n += writeUtf8(pRes+n, c.aResult[i]);
|
||
+ }
|
||
+ pRes[n] = 0;
|
||
+ sqlite3_result_text(context, (const char*)pRes, n, sqlite3_free);
|
||
+ }
|
||
+ }
|
||
+ sqlite3_finalize(c.pStmt);
|
||
+ sqlite3_free(c.aResult);
|
||
+}
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_nextchar_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "next_char", 3, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
|
||
+ nextCharFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/percentile.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/percentile.c 2015-01-31 00:31:56.425144800 +0100
|
||
@@ -0,0 +1,233 @@
|
||
+/*
|
||
+** 2013-05-28
|
||
+**
|
||
+** 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 contains code to implement the percentile(Y,P) SQL function
|
||
+** as described below:
|
||
+**
|
||
+** (1) The percentile(Y,P) function is an aggregate function taking
|
||
+** exactly two arguments.
|
||
+**
|
||
+** (2) If the P argument to percentile(Y,P) is not the same for every
|
||
+** row in the aggregate then an error is thrown. The word "same"
|
||
+** in the previous sentence means that the value differ by less
|
||
+** than 0.001.
|
||
+**
|
||
+** (3) If the P argument to percentile(Y,P) evaluates to anything other
|
||
+** than a number in the range of 0.0 to 100.0 inclusive then an
|
||
+** error is thrown.
|
||
+**
|
||
+** (4) If any Y argument to percentile(Y,P) evaluates to a value that
|
||
+** is not NULL and is not numeric then an error is thrown.
|
||
+**
|
||
+** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
|
||
+** infinity then an error is thrown. (SQLite always interprets NaN
|
||
+** values as NULL.)
|
||
+**
|
||
+** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
|
||
+** including CASE WHEN expressions.
|
||
+**
|
||
+** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
|
||
+** one million (1,000,000) rows.
|
||
+**
|
||
+** (8) If there are no non-NULL values for Y, then percentile(Y,P)
|
||
+** returns NULL.
|
||
+**
|
||
+** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
|
||
+** returns the one Y value.
|
||
+**
|
||
+** (10) If there N non-NULL values of Y where N is two or more and
|
||
+** the Y values are ordered from least to greatest and a graph is
|
||
+** drawn from 0 to N-1 such that the height of the graph at J is
|
||
+** the J-th Y value and such that straight lines are drawn between
|
||
+** adjacent Y values, then the percentile(Y,P) function returns
|
||
+** the height of the graph at P*(N-1)/100.
|
||
+**
|
||
+** (11) The percentile(Y,P) function always returns either a floating
|
||
+** point number or NULL.
|
||
+**
|
||
+** (12) The percentile(Y,P) is implemented as a single C99 source-code
|
||
+** file that compiles into a shared-library or DLL that can be loaded
|
||
+** into SQLite using the sqlite3_load_extension() interface.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+#include <stdlib.h>
|
||
+
|
||
+/* The following object is the session context for a single percentile()
|
||
+** function. We have to remember all input Y values until the very end.
|
||
+** Those values are accumulated in the Percentile.a[] array.
|
||
+*/
|
||
+typedef struct Percentile Percentile;
|
||
+struct Percentile {
|
||
+ unsigned nAlloc; /* Number of slots allocated for a[] */
|
||
+ unsigned nUsed; /* Number of slots actually used in a[] */
|
||
+ double rPct; /* 1.0 more than the value for P */
|
||
+ double *a; /* Array of Y values */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Return TRUE if the input floating-point number is an infinity.
|
||
+*/
|
||
+static int isInfinity(double r){
|
||
+ sqlite3_uint64 u;
|
||
+ assert( sizeof(u)==sizeof(r) );
|
||
+ memcpy(&u, &r, sizeof(u));
|
||
+ return ((u>>52)&0x7ff)==0x7ff;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE if two doubles differ by 0.001 or less
|
||
+*/
|
||
+static int sameValue(double a, double b){
|
||
+ a -= b;
|
||
+ return a>=-0.001 && a<=0.001;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The "step" function for percentile(Y,P) is called once for each
|
||
+** input row.
|
||
+*/
|
||
+static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||
+ Percentile *p;
|
||
+ double rPct;
|
||
+ int eType;
|
||
+ double y;
|
||
+ assert( argc==2 );
|
||
+
|
||
+ /* Requirement 3: P must be a number between 0 and 100 */
|
||
+ eType = sqlite3_value_numeric_type(argv[1]);
|
||
+ rPct = sqlite3_value_double(argv[1]);
|
||
+ if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) ||
|
||
+ ((rPct = sqlite3_value_double(argv[1]))<0.0 || rPct>100.0) ){
|
||
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
|
||
+ "a number between 0.0 and 100.0", -1);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Allocate the session context. */
|
||
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
||
+ if( p==0 ) return;
|
||
+
|
||
+ /* Remember the P value. Throw an error if the P value is different
|
||
+ ** from any prior row, per Requirement (2). */
|
||
+ if( p->rPct==0.0 ){
|
||
+ p->rPct = rPct+1.0;
|
||
+ }else if( !sameValue(p->rPct,rPct+1.0) ){
|
||
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
|
||
+ "same for all input rows", -1);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Ignore rows for which Y is NULL */
|
||
+ eType = sqlite3_value_type(argv[0]);
|
||
+ if( eType==SQLITE_NULL ) return;
|
||
+
|
||
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
|
||
+ ** Requirement 4 */
|
||
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
||
+ sqlite3_result_error(pCtx, "1st argument to percentile() is not "
|
||
+ "numeric", -1);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Throw an error if the Y value is infinity or NaN */
|
||
+ y = sqlite3_value_double(argv[0]);
|
||
+ if( isInfinity(y) ){
|
||
+ sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Allocate and store the Y */
|
||
+ if( p->nUsed>=p->nAlloc ){
|
||
+ unsigned n = p->nAlloc*2 + 250;
|
||
+ double *a = sqlite3_realloc(p->a, sizeof(double)*n);
|
||
+ if( a==0 ){
|
||
+ sqlite3_free(p->a);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+ sqlite3_result_error_nomem(pCtx);
|
||
+ return;
|
||
+ }
|
||
+ p->nAlloc = n;
|
||
+ p->a = a;
|
||
+ }
|
||
+ p->a[p->nUsed++] = y;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compare to doubles for sorting using qsort()
|
||
+*/
|
||
+static int doubleCmp(const void *pA, const void *pB){
|
||
+ double a = *(double*)pA;
|
||
+ double b = *(double*)pB;
|
||
+ if( a==b ) return 0;
|
||
+ if( a<b ) return -1;
|
||
+ return +1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Called to compute the final output of percentile() and to clean
|
||
+** up all allocated memory.
|
||
+*/
|
||
+static void percentFinal(sqlite3_context *pCtx){
|
||
+ Percentile *p;
|
||
+ unsigned i1, i2;
|
||
+ double v1, v2;
|
||
+ double ix, vx;
|
||
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
|
||
+ if( p==0 ) return;
|
||
+ if( p->a==0 ) return;
|
||
+ if( p->nUsed ){
|
||
+ qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
|
||
+ ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
|
||
+ i1 = (unsigned)ix;
|
||
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
||
+ v1 = p->a[i1];
|
||
+ v2 = p->a[i2];
|
||
+ vx = v1 + (v2-v1)*(ix-i1);
|
||
+ sqlite3_result_double(pCtx, vx);
|
||
+ }
|
||
+ sqlite3_free(p->a);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_percentile_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
||
+ 0, percentStep, percentFinal);
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
||
+ 0, percentStep, percentFinal);
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/regexp.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/regexp.c 2015-01-31 00:31:56.438145600 +0100
|
||
@@ -0,0 +1,773 @@
|
||
+/*
|
||
+** 2012-11-13
|
||
+**
|
||
+** 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.
|
||
+**
|
||
+******************************************************************************
|
||
+**
|
||
+** The code in this file implements a compact but reasonably
|
||
+** efficient regular-expression matcher for posix extended regular
|
||
+** expressions against UTF8 text.
|
||
+**
|
||
+** This file is an SQLite extension. It registers a single function
|
||
+** named "regexp(A,B)" where A is the regular expression and B is the
|
||
+** string to be matched. By registering this function, SQLite will also
|
||
+** then implement the "B regexp A" operator. Note that with the function
|
||
+** the regular expression comes first, but with the operator it comes
|
||
+** second.
|
||
+**
|
||
+** The following regular expression syntax is supported:
|
||
+**
|
||
+** X* zero or more occurrences of X
|
||
+** X+ one or more occurrences of X
|
||
+** X? zero or one occurrences of X
|
||
+** X{p,q} between p and q occurrences of X
|
||
+** (X) match X
|
||
+** X|Y X or Y
|
||
+** ^X X occurring at the beginning of the string
|
||
+** X$ X occurring at the end of the string
|
||
+** . Match any single character
|
||
+** \c Character c where c is one of \{}()[]|*+?.
|
||
+** \c C-language escapes for c in afnrtv. ex: \t or \n
|
||
+** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
|
||
+** \xXX Where XX is exactly 2 hex digits, unicode value XX
|
||
+** [abc] Any single character from the set abc
|
||
+** [^abc] Any single character not in the set abc
|
||
+** [a-z] Any single character in the range a-z
|
||
+** [^a-z] Any single character not in the range a-z
|
||
+** \b Word boundary
|
||
+** \w Word character. [A-Za-z0-9_]
|
||
+** \W Non-word character
|
||
+** \d Digit
|
||
+** \D Non-digit
|
||
+** \s Whitespace character
|
||
+** \S Non-whitespace character
|
||
+**
|
||
+** A nondeterministic finite automaton (NFA) is used for matching, so the
|
||
+** performance is bounded by O(N*M) where N is the size of the regular
|
||
+** expression and M is the size of the input string. The matcher never
|
||
+** exhibits exponential behavior. Note that the X{p,q} operator expands
|
||
+** to p copies of X following by q-p copies of X? and that the size of the
|
||
+** regular expression in the O(N*M) performance bound is computed after
|
||
+** this expansion.
|
||
+*/
|
||
+#include <string.h>
|
||
+#include <stdlib.h>
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+
|
||
+/*
|
||
+** The following #defines change the names of some functions implemented in
|
||
+** this file to prevent name collisions with C-library functions of the
|
||
+** same name.
|
||
+*/
|
||
+#define re_match sqlite3re_match
|
||
+#define re_compile sqlite3re_compile
|
||
+#define re_free sqlite3re_free
|
||
+
|
||
+/* The end-of-input character */
|
||
+#define RE_EOF 0 /* End of input */
|
||
+
|
||
+/* The NFA is implemented as sequence of opcodes taken from the following
|
||
+** set. Each opcode has a single integer argument.
|
||
+*/
|
||
+#define RE_OP_MATCH 1 /* Match the one character in the argument */
|
||
+#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
|
||
+#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
|
||
+#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
|
||
+#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
|
||
+#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
|
||
+#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
|
||
+#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
|
||
+#define RE_OP_CC_VALUE 9 /* Single value in a character class */
|
||
+#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
|
||
+#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
|
||
+#define RE_OP_NOTWORD 12 /* Not a perl word character */
|
||
+#define RE_OP_DIGIT 13 /* digit: [0-9] */
|
||
+#define RE_OP_NOTDIGIT 14 /* Not a digit */
|
||
+#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
|
||
+#define RE_OP_NOTSPACE 16 /* Not a digit */
|
||
+#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
|
||
+
|
||
+/* Each opcode is a "state" in the NFA */
|
||
+typedef unsigned short ReStateNumber;
|
||
+
|
||
+/* Because this is an NFA and not a DFA, multiple states can be active at
|
||
+** once. An instance of the following object records all active states in
|
||
+** the NFA. The implementation is optimized for the common case where the
|
||
+** number of actives states is small.
|
||
+*/
|
||
+typedef struct ReStateSet {
|
||
+ unsigned nState; /* Number of current states */
|
||
+ ReStateNumber *aState; /* Current states */
|
||
+} ReStateSet;
|
||
+
|
||
+/* An input string read one character at a time.
|
||
+*/
|
||
+typedef struct ReInput ReInput;
|
||
+struct ReInput {
|
||
+ const unsigned char *z; /* All text */
|
||
+ int i; /* Next byte to read */
|
||
+ int mx; /* EOF when i>=mx */
|
||
+};
|
||
+
|
||
+/* A compiled NFA (or an NFA that is in the process of being compiled) is
|
||
+** an instance of the following object.
|
||
+*/
|
||
+typedef struct ReCompiled ReCompiled;
|
||
+struct ReCompiled {
|
||
+ ReInput sIn; /* Regular expression text */
|
||
+ const char *zErr; /* Error message to return */
|
||
+ char *aOp; /* Operators for the virtual machine */
|
||
+ int *aArg; /* Arguments to each operator */
|
||
+ unsigned (*xNextChar)(ReInput*); /* Next character function */
|
||
+ unsigned char zInit[12]; /* Initial text to match */
|
||
+ int nInit; /* Number of characters in zInit */
|
||
+ unsigned nState; /* Number of entries in aOp[] and aArg[] */
|
||
+ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
|
||
+};
|
||
+
|
||
+/* Add a state to the given state set if it is not already there */
|
||
+static void re_add_state(ReStateSet *pSet, int newState){
|
||
+ unsigned i;
|
||
+ for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
|
||
+ pSet->aState[pSet->nState++] = newState;
|
||
+}
|
||
+
|
||
+/* Extract the next unicode character from *pzIn and return it. Advance
|
||
+** *pzIn to the first byte past the end of the character returned. To
|
||
+** be clear: this routine converts utf8 to unicode. This routine is
|
||
+** optimized for the common case where the next character is a single byte.
|
||
+*/
|
||
+static unsigned re_next_char(ReInput *p){
|
||
+ unsigned c;
|
||
+ if( p->i>=p->mx ) return 0;
|
||
+ c = p->z[p->i++];
|
||
+ if( c>=0x80 ){
|
||
+ if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
|
||
+ c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
|
||
+ if( c<0x80 ) c = 0xfffd;
|
||
+ }else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
|
||
+ && (p->z[p->i+1]&0xc0)==0x80 ){
|
||
+ c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||
+ p->i += 2;
|
||
+ if( c<=0x3ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||
+ }else if( (c&0xf8)==0xf0 && p->i+3<p->mx && (p->z[p->i]&0xc0)==0x80
|
||
+ && (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||
+ c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||
+ | (p->z[p->i+2]&0x3f);
|
||
+ p->i += 3;
|
||
+ if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
|
||
+ }else{
|
||
+ c = 0xfffd;
|
||
+ }
|
||
+ }
|
||
+ return c;
|
||
+}
|
||
+static unsigned re_next_char_nocase(ReInput *p){
|
||
+ unsigned c = re_next_char(p);
|
||
+ if( c>='A' && c<='Z' ) c += 'a' - 'A';
|
||
+ return c;
|
||
+}
|
||
+
|
||
+/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
|
||
+static int re_word_char(int c){
|
||
+ return (c>='0' && c<='9') || (c>='a' && c<='z')
|
||
+ || (c>='A' && c<='Z') || c=='_';
|
||
+}
|
||
+
|
||
+/* Return true if c is a "digit" character: [0-9] */
|
||
+static int re_digit_char(int c){
|
||
+ return (c>='0' && c<='9');
|
||
+}
|
||
+
|
||
+/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
|
||
+static int re_space_char(int c){
|
||
+ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
|
||
+}
|
||
+
|
||
+/* Run a compiled regular expression on the zero-terminated input
|
||
+** string zIn[]. Return true on a match and false if there is no match.
|
||
+*/
|
||
+int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
|
||
+ ReStateSet aStateSet[2], *pThis, *pNext;
|
||
+ ReStateNumber aSpace[100];
|
||
+ ReStateNumber *pToFree;
|
||
+ unsigned int i = 0;
|
||
+ unsigned int iSwap = 0;
|
||
+ int c = RE_EOF+1;
|
||
+ int cPrev = 0;
|
||
+ int rc = 0;
|
||
+ ReInput in;
|
||
+
|
||
+ in.z = zIn;
|
||
+ in.i = 0;
|
||
+ in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
|
||
+
|
||
+ /* Look for the initial prefix match, if there is one. */
|
||
+ if( pRe->nInit ){
|
||
+ unsigned char x = pRe->zInit[0];
|
||
+ while( in.i+pRe->nInit<=in.mx
|
||
+ && (zIn[in.i]!=x ||
|
||
+ strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
|
||
+ ){
|
||
+ in.i++;
|
||
+ }
|
||
+ if( in.i+pRe->nInit>in.mx ) return 0;
|
||
+ }
|
||
+
|
||
+ if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
|
||
+ pToFree = 0;
|
||
+ aStateSet[0].aState = aSpace;
|
||
+ }else{
|
||
+ pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState );
|
||
+ if( pToFree==0 ) return -1;
|
||
+ aStateSet[0].aState = pToFree;
|
||
+ }
|
||
+ aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
|
||
+ pNext = &aStateSet[1];
|
||
+ pNext->nState = 0;
|
||
+ re_add_state(pNext, 0);
|
||
+ while( c!=RE_EOF && pNext->nState>0 ){
|
||
+ cPrev = c;
|
||
+ c = pRe->xNextChar(&in);
|
||
+ pThis = pNext;
|
||
+ pNext = &aStateSet[iSwap];
|
||
+ iSwap = 1 - iSwap;
|
||
+ pNext->nState = 0;
|
||
+ for(i=0; i<pThis->nState; i++){
|
||
+ int x = pThis->aState[i];
|
||
+ switch( pRe->aOp[x] ){
|
||
+ case RE_OP_MATCH: {
|
||
+ if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_ANY: {
|
||
+ re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_WORD: {
|
||
+ if( re_word_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_NOTWORD: {
|
||
+ if( !re_word_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_DIGIT: {
|
||
+ if( re_digit_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_NOTDIGIT: {
|
||
+ if( !re_digit_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_SPACE: {
|
||
+ if( re_space_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_NOTSPACE: {
|
||
+ if( !re_space_char(c) ) re_add_state(pNext, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_BOUNDARY: {
|
||
+ if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_ANYSTAR: {
|
||
+ re_add_state(pNext, x);
|
||
+ re_add_state(pThis, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_FORK: {
|
||
+ re_add_state(pThis, x+pRe->aArg[x]);
|
||
+ re_add_state(pThis, x+1);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_GOTO: {
|
||
+ re_add_state(pThis, x+pRe->aArg[x]);
|
||
+ break;
|
||
+ }
|
||
+ case RE_OP_ACCEPT: {
|
||
+ rc = 1;
|
||
+ goto re_match_end;
|
||
+ }
|
||
+ case RE_OP_CC_INC:
|
||
+ case RE_OP_CC_EXC: {
|
||
+ int j = 1;
|
||
+ int n = pRe->aArg[x];
|
||
+ int hit = 0;
|
||
+ for(j=1; j>0 && j<n; j++){
|
||
+ if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
|
||
+ if( pRe->aArg[x+j]==c ){
|
||
+ hit = 1;
|
||
+ j = -1;
|
||
+ }
|
||
+ }else{
|
||
+ if( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1]>=c ){
|
||
+ hit = 1;
|
||
+ j = -1;
|
||
+ }else{
|
||
+ j++;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
|
||
+ if( hit ) re_add_state(pNext, x+n);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ for(i=0; i<pNext->nState; i++){
|
||
+ if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
|
||
+ }
|
||
+re_match_end:
|
||
+ sqlite3_free(pToFree);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/* Resize the opcode and argument arrays for an RE under construction.
|
||
+*/
|
||
+static int re_resize(ReCompiled *p, int N){
|
||
+ char *aOp;
|
||
+ int *aArg;
|
||
+ aOp = sqlite3_realloc(p->aOp, N*sizeof(p->aOp[0]));
|
||
+ if( aOp==0 ) return 1;
|
||
+ p->aOp = aOp;
|
||
+ aArg = sqlite3_realloc(p->aArg, N*sizeof(p->aArg[0]));
|
||
+ if( aArg==0 ) return 1;
|
||
+ p->aArg = aArg;
|
||
+ p->nAlloc = N;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Insert a new opcode and argument into an RE under construction. The
|
||
+** insertion point is just prior to existing opcode iBefore.
|
||
+*/
|
||
+static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
|
||
+ int i;
|
||
+ if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
|
||
+ for(i=p->nState; i>iBefore; i--){
|
||
+ p->aOp[i] = p->aOp[i-1];
|
||
+ p->aArg[i] = p->aArg[i-1];
|
||
+ }
|
||
+ p->nState++;
|
||
+ p->aOp[iBefore] = op;
|
||
+ p->aArg[iBefore] = arg;
|
||
+ return iBefore;
|
||
+}
|
||
+
|
||
+/* Append a new opcode and argument to the end of the RE under construction.
|
||
+*/
|
||
+static int re_append(ReCompiled *p, int op, int arg){
|
||
+ return re_insert(p, p->nState, op, arg);
|
||
+}
|
||
+
|
||
+/* Make a copy of N opcodes starting at iStart onto the end of the RE
|
||
+** under construction.
|
||
+*/
|
||
+static void re_copy(ReCompiled *p, int iStart, int N){
|
||
+ if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
|
||
+ memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
|
||
+ memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
|
||
+ p->nState += N;
|
||
+}
|
||
+
|
||
+/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
|
||
+** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If
|
||
+** c is not a hex digit *pV is unchanged.
|
||
+*/
|
||
+static int re_hex(int c, int *pV){
|
||
+ if( c>='0' && c<='9' ){
|
||
+ c -= '0';
|
||
+ }else if( c>='a' && c<='f' ){
|
||
+ c -= 'a' - 10;
|
||
+ }else if( c>='A' && c<='F' ){
|
||
+ c -= 'A' - 10;
|
||
+ }else{
|
||
+ return 0;
|
||
+ }
|
||
+ *pV = (*pV)*16 + (c & 0xff);
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+/* A backslash character has been seen, read the next character and
|
||
+** return its interpretation.
|
||
+*/
|
||
+static unsigned re_esc_char(ReCompiled *p){
|
||
+ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
|
||
+ static const char zTrans[] = "\a\f\n\r\t\v";
|
||
+ int i, v = 0;
|
||
+ char c;
|
||
+ if( p->sIn.i>=p->sIn.mx ) return 0;
|
||
+ c = p->sIn.z[p->sIn.i];
|
||
+ if( c=='u' && p->sIn.i+4<p->sIn.mx ){
|
||
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||
+ if( re_hex(zIn[1],&v)
|
||
+ && re_hex(zIn[2],&v)
|
||
+ && re_hex(zIn[3],&v)
|
||
+ && re_hex(zIn[4],&v)
|
||
+ ){
|
||
+ p->sIn.i += 5;
|
||
+ return v;
|
||
+ }
|
||
+ }
|
||
+ if( c=='x' && p->sIn.i+2<p->sIn.mx ){
|
||
+ const unsigned char *zIn = p->sIn.z + p->sIn.i;
|
||
+ if( re_hex(zIn[1],&v)
|
||
+ && re_hex(zIn[2],&v)
|
||
+ ){
|
||
+ p->sIn.i += 3;
|
||
+ return v;
|
||
+ }
|
||
+ }
|
||
+ for(i=0; zEsc[i] && zEsc[i]!=c; i++){}
|
||
+ if( zEsc[i] ){
|
||
+ if( i<6 ) c = zTrans[i];
|
||
+ p->sIn.i++;
|
||
+ }else{
|
||
+ p->zErr = "unknown \\ escape";
|
||
+ }
|
||
+ return c;
|
||
+}
|
||
+
|
||
+/* Forward declaration */
|
||
+static const char *re_subcompile_string(ReCompiled*);
|
||
+
|
||
+/* Peek at the next byte of input */
|
||
+static unsigned char rePeek(ReCompiled *p){
|
||
+ return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0;
|
||
+}
|
||
+
|
||
+/* Compile RE text into a sequence of opcodes. Continue up to the
|
||
+** first unmatched ")" character, then return. If an error is found,
|
||
+** return a pointer to the error message string.
|
||
+*/
|
||
+static const char *re_subcompile_re(ReCompiled *p){
|
||
+ const char *zErr;
|
||
+ int iStart, iEnd, iGoto;
|
||
+ iStart = p->nState;
|
||
+ zErr = re_subcompile_string(p);
|
||
+ if( zErr ) return zErr;
|
||
+ while( rePeek(p)=='|' ){
|
||
+ iEnd = p->nState;
|
||
+ re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
|
||
+ iGoto = re_append(p, RE_OP_GOTO, 0);
|
||
+ p->sIn.i++;
|
||
+ zErr = re_subcompile_string(p);
|
||
+ if( zErr ) return zErr;
|
||
+ p->aArg[iGoto] = p->nState - iGoto;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Compile an element of regular expression text (anything that can be
|
||
+** an operand to the "|" operator). Return NULL on success or a pointer
|
||
+** to the error message if there is a problem.
|
||
+*/
|
||
+static const char *re_subcompile_string(ReCompiled *p){
|
||
+ int iPrev = -1;
|
||
+ int iStart;
|
||
+ unsigned c;
|
||
+ const char *zErr;
|
||
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||
+ iStart = p->nState;
|
||
+ switch( c ){
|
||
+ case '|':
|
||
+ case '$':
|
||
+ case ')': {
|
||
+ p->sIn.i--;
|
||
+ return 0;
|
||
+ }
|
||
+ case '(': {
|
||
+ zErr = re_subcompile_re(p);
|
||
+ if( zErr ) return zErr;
|
||
+ if( rePeek(p)!=')' ) return "unmatched '('";
|
||
+ p->sIn.i++;
|
||
+ break;
|
||
+ }
|
||
+ case '.': {
|
||
+ if( rePeek(p)=='*' ){
|
||
+ re_append(p, RE_OP_ANYSTAR, 0);
|
||
+ p->sIn.i++;
|
||
+ }else{
|
||
+ re_append(p, RE_OP_ANY, 0);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case '*': {
|
||
+ if( iPrev<0 ) return "'*' without operand";
|
||
+ re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1);
|
||
+ re_append(p, RE_OP_FORK, iPrev - p->nState + 1);
|
||
+ break;
|
||
+ }
|
||
+ case '+': {
|
||
+ if( iPrev<0 ) return "'+' without operand";
|
||
+ re_append(p, RE_OP_FORK, iPrev - p->nState);
|
||
+ break;
|
||
+ }
|
||
+ case '?': {
|
||
+ if( iPrev<0 ) return "'?' without operand";
|
||
+ re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
|
||
+ break;
|
||
+ }
|
||
+ case '{': {
|
||
+ int m = 0, n = 0;
|
||
+ int sz, j;
|
||
+ if( iPrev<0 ) return "'{m,n}' without operand";
|
||
+ while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
|
||
+ n = m;
|
||
+ if( c==',' ){
|
||
+ p->sIn.i++;
|
||
+ n = 0;
|
||
+ while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
|
||
+ }
|
||
+ if( c!='}' ) return "unmatched '{'";
|
||
+ if( n>0 && n<m ) return "n less than m in '{m,n}'";
|
||
+ p->sIn.i++;
|
||
+ sz = p->nState - iPrev;
|
||
+ if( m==0 ){
|
||
+ if( n==0 ) return "both m and n are zero in '{m,n}'";
|
||
+ re_insert(p, iPrev, RE_OP_FORK, sz+1);
|
||
+ n--;
|
||
+ }else{
|
||
+ for(j=1; j<m; j++) re_copy(p, iPrev, sz);
|
||
+ }
|
||
+ for(j=m; j<n; j++){
|
||
+ re_append(p, RE_OP_FORK, sz+1);
|
||
+ re_copy(p, iPrev, sz);
|
||
+ }
|
||
+ if( n==0 && m>0 ){
|
||
+ re_append(p, RE_OP_FORK, -sz);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case '[': {
|
||
+ int iFirst = p->nState;
|
||
+ if( rePeek(p)=='^' ){
|
||
+ re_append(p, RE_OP_CC_EXC, 0);
|
||
+ p->sIn.i++;
|
||
+ }else{
|
||
+ re_append(p, RE_OP_CC_INC, 0);
|
||
+ }
|
||
+ while( (c = p->xNextChar(&p->sIn))!=0 ){
|
||
+ if( c=='[' && rePeek(p)==':' ){
|
||
+ return "POSIX character classes not supported";
|
||
+ }
|
||
+ if( c=='\\' ) c = re_esc_char(p);
|
||
+ if( rePeek(p)=='-' ){
|
||
+ re_append(p, RE_OP_CC_RANGE, c);
|
||
+ p->sIn.i++;
|
||
+ c = p->xNextChar(&p->sIn);
|
||
+ if( c=='\\' ) c = re_esc_char(p);
|
||
+ re_append(p, RE_OP_CC_RANGE, c);
|
||
+ }else{
|
||
+ re_append(p, RE_OP_CC_VALUE, c);
|
||
+ }
|
||
+ if( rePeek(p)==']' ){ p->sIn.i++; break; }
|
||
+ }
|
||
+ if( c==0 ) return "unclosed '['";
|
||
+ p->aArg[iFirst] = p->nState - iFirst;
|
||
+ break;
|
||
+ }
|
||
+ case '\\': {
|
||
+ int specialOp = 0;
|
||
+ switch( rePeek(p) ){
|
||
+ case 'b': specialOp = RE_OP_BOUNDARY; break;
|
||
+ case 'd': specialOp = RE_OP_DIGIT; break;
|
||
+ case 'D': specialOp = RE_OP_NOTDIGIT; break;
|
||
+ case 's': specialOp = RE_OP_SPACE; break;
|
||
+ case 'S': specialOp = RE_OP_NOTSPACE; break;
|
||
+ case 'w': specialOp = RE_OP_WORD; break;
|
||
+ case 'W': specialOp = RE_OP_NOTWORD; break;
|
||
+ }
|
||
+ if( specialOp ){
|
||
+ p->sIn.i++;
|
||
+ re_append(p, specialOp, 0);
|
||
+ }else{
|
||
+ c = re_esc_char(p);
|
||
+ re_append(p, RE_OP_MATCH, c);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ default: {
|
||
+ re_append(p, RE_OP_MATCH, c);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ iPrev = iStart;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Free and reclaim all the memory used by a previously compiled
|
||
+** regular expression. Applications should invoke this routine once
|
||
+** for every call to re_compile() to avoid memory leaks.
|
||
+*/
|
||
+void re_free(ReCompiled *pRe){
|
||
+ if( pRe ){
|
||
+ sqlite3_free(pRe->aOp);
|
||
+ sqlite3_free(pRe->aArg);
|
||
+ sqlite3_free(pRe);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compile a textual regular expression in zIn[] into a compiled regular
|
||
+** expression suitable for us by re_match() and return a pointer to the
|
||
+** compiled regular expression in *ppRe. Return NULL on success or an
|
||
+** error message if something goes wrong.
|
||
+*/
|
||
+const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
|
||
+ ReCompiled *pRe;
|
||
+ const char *zErr;
|
||
+ int i, j;
|
||
+
|
||
+ *ppRe = 0;
|
||
+ pRe = sqlite3_malloc( sizeof(*pRe) );
|
||
+ if( pRe==0 ){
|
||
+ return "out of memory";
|
||
+ }
|
||
+ memset(pRe, 0, sizeof(*pRe));
|
||
+ pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
|
||
+ if( re_resize(pRe, 30) ){
|
||
+ re_free(pRe);
|
||
+ return "out of memory";
|
||
+ }
|
||
+ if( zIn[0]=='^' ){
|
||
+ zIn++;
|
||
+ }else{
|
||
+ re_append(pRe, RE_OP_ANYSTAR, 0);
|
||
+ }
|
||
+ pRe->sIn.z = (unsigned char*)zIn;
|
||
+ pRe->sIn.i = 0;
|
||
+ pRe->sIn.mx = (int)strlen(zIn);
|
||
+ zErr = re_subcompile_re(pRe);
|
||
+ if( zErr ){
|
||
+ re_free(pRe);
|
||
+ return zErr;
|
||
+ }
|
||
+ if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
|
||
+ re_append(pRe, RE_OP_MATCH, RE_EOF);
|
||
+ re_append(pRe, RE_OP_ACCEPT, 0);
|
||
+ *ppRe = pRe;
|
||
+ }else if( pRe->sIn.i>=pRe->sIn.mx ){
|
||
+ re_append(pRe, RE_OP_ACCEPT, 0);
|
||
+ *ppRe = pRe;
|
||
+ }else{
|
||
+ re_free(pRe);
|
||
+ return "unrecognized character";
|
||
+ }
|
||
+
|
||
+ /* The following is a performance optimization. If the regex begins with
|
||
+ ** ".*" (if the input regex lacks an initial "^") and afterwards there are
|
||
+ ** one or more matching characters, enter those matching characters into
|
||
+ ** zInit[]. The re_match() routine can then search ahead in the input
|
||
+ ** string looking for the initial match without having to run the whole
|
||
+ ** regex engine over the string. Do not worry able trying to match
|
||
+ ** unicode characters beyond plane 0 - those are very rare and this is
|
||
+ ** just an optimization. */
|
||
+ if( pRe->aOp[0]==RE_OP_ANYSTAR ){
|
||
+ for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
|
||
+ unsigned x = pRe->aArg[i];
|
||
+ if( x<=127 ){
|
||
+ pRe->zInit[j++] = x;
|
||
+ }else if( x<=0xfff ){
|
||
+ pRe->zInit[j++] = 0xc0 | (x>>6);
|
||
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||
+ }else if( x<=0xffff ){
|
||
+ pRe->zInit[j++] = 0xd0 | (x>>12);
|
||
+ pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
||
+ pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||
+ }else{
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
||
+ pRe->nInit = j;
|
||
+ }
|
||
+ return pRe->zErr;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the regexp() SQL function. This function implements
|
||
+** the build-in REGEXP operator. The first argument to the function is the
|
||
+** pattern and the second argument is the string. So, the SQL statements:
|
||
+**
|
||
+** A REGEXP B
|
||
+**
|
||
+** is implemented as regexp(B,A).
|
||
+*/
|
||
+static void re_sql_func(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ ReCompiled *pRe; /* Compiled regular expression */
|
||
+ const char *zPattern; /* The regular expression */
|
||
+ const unsigned char *zStr;/* String being searched */
|
||
+ const char *zErr; /* Compile error message */
|
||
+ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
|
||
+
|
||
+ pRe = sqlite3_get_auxdata(context, 0);
|
||
+ if( pRe==0 ){
|
||
+ zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||
+ if( zPattern==0 ) return;
|
||
+ zErr = re_compile(&pRe, zPattern, 0);
|
||
+ if( zErr ){
|
||
+ re_free(pRe);
|
||
+ sqlite3_result_error(context, zErr, -1);
|
||
+ return;
|
||
+ }
|
||
+ if( pRe==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ return;
|
||
+ }
|
||
+ setAux = 1;
|
||
+ }
|
||
+ zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
|
||
+ if( zStr!=0 ){
|
||
+ sqlite3_result_int(context, re_match(pRe, zStr, -1));
|
||
+ }
|
||
+ if( setAux ){
|
||
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Invoke this routine to register the regexp() function with the
|
||
+** SQLite database connection.
|
||
+*/
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_regexp_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
||
+ re_sql_func, 0, 0);
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
|
||
+ re_sql_func, 0, 0);
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/rot13.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/rot13.c 2015-01-31 00:31:56.456146600 +0100
|
||
@@ -0,0 +1,131 @@
|
||
+/*
|
||
+** 2013-05-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 SQLite extension implements a rot13() function and a rot13
|
||
+** collating sequence.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+
|
||
+/*
|
||
+** Perform rot13 encoding on a single ASCII character.
|
||
+*/
|
||
+static unsigned char rot13(unsigned char c){
|
||
+ if( c>='a' && c<='z' ){
|
||
+ c += 13;
|
||
+ if( c>'z' ) c -= 26;
|
||
+ }else if( c>='A' && c<='Z' ){
|
||
+ c += 13;
|
||
+ if( c>'Z' ) c -= 26;
|
||
+ }
|
||
+ return c;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the rot13() function.
|
||
+**
|
||
+** Rotate ASCII alphabetic characters by 13 character positions.
|
||
+** Non-ASCII characters are unchanged. rot13(rot13(X)) should always
|
||
+** equal X.
|
||
+*/
|
||
+static void rot13func(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zIn;
|
||
+ int nIn;
|
||
+ unsigned char *zOut;
|
||
+ char *zToFree = 0;
|
||
+ int i;
|
||
+ char zTemp[100];
|
||
+ assert( argc==1 );
|
||
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||
+ zIn = (const unsigned char*)sqlite3_value_text(argv[0]);
|
||
+ nIn = sqlite3_value_bytes(argv[0]);
|
||
+ if( nIn<sizeof(zTemp)-1 ){
|
||
+ zOut = zTemp;
|
||
+ }else{
|
||
+ zOut = zToFree = sqlite3_malloc( nIn+1 );
|
||
+ if( zOut==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+ for(i=0; i<nIn; i++) zOut[i] = rot13(zIn[i]);
|
||
+ zOut[i] = 0;
|
||
+ sqlite3_result_text(context, (char*)zOut, i, SQLITE_TRANSIENT);
|
||
+ sqlite3_free(zToFree);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implement the rot13 collating sequence so that if
|
||
+**
|
||
+** x=y COLLATE rot13
|
||
+**
|
||
+** Then
|
||
+**
|
||
+** rot13(x)=rot13(y) COLLATE binary
|
||
+*/
|
||
+static int rot13CollFunc(
|
||
+ void *notUsed,
|
||
+ int nKey1, const void *pKey1,
|
||
+ int nKey2, const void *pKey2
|
||
+){
|
||
+ const char *zA = (const char*)pKey1;
|
||
+ const char *zB = (const char*)pKey2;
|
||
+ int i, x;
|
||
+ for(i=0; i<nKey1 && i<nKey2; i++){
|
||
+ x = (int)rot13(zA[i]) - (int)rot13(zB[i]);
|
||
+ if( x!=0 ) return x;
|
||
+ }
|
||
+ return nKey1 - nKey2;
|
||
+}
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_rot_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
|
||
+ rot13func, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "rot13", 1, SQLITE_UTF8, 0,
|
||
+ rot13func, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/showauth.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/showauth.c 2015-01-31 00:31:56.463147000 +0100
|
||
@@ -0,0 +1,103 @@
|
||
+/*
|
||
+** 2014-09-21
|
||
+**
|
||
+** 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 SQLite extension adds a debug "authorizer" callback to the database
|
||
+** connection. The callback merely writes the authorization request to
|
||
+** standard output and returns SQLITE_OK.
|
||
+**
|
||
+** This extension can be used (for example) in the command-line shell to
|
||
+** trace the operation of the authorizer.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <stdio.h>
|
||
+
|
||
+/*
|
||
+** Display the authorization request
|
||
+*/
|
||
+static int authCallback(
|
||
+ void *pClientData,
|
||
+ int op,
|
||
+ const char *z1,
|
||
+ const char *z2,
|
||
+ const char *z3,
|
||
+ const char *z4
|
||
+){
|
||
+ const char *zOp;
|
||
+ char zOpSpace[50];
|
||
+ switch( op ){
|
||
+ case SQLITE_CREATE_INDEX: zOp = "CREATE_INDEX"; break;
|
||
+ case SQLITE_CREATE_TABLE: zOp = "CREATE_TABLE"; break;
|
||
+ case SQLITE_CREATE_TEMP_INDEX: zOp = "CREATE_TEMP_INDEX"; break;
|
||
+ case SQLITE_CREATE_TEMP_TABLE: zOp = "CREATE_TEMP_TABLE"; break;
|
||
+ case SQLITE_CREATE_TEMP_TRIGGER: zOp = "CREATE_TEMP_TRIGGER"; break;
|
||
+ case SQLITE_CREATE_TEMP_VIEW: zOp = "CREATE_TEMP_VIEW"; break;
|
||
+ case SQLITE_CREATE_TRIGGER: zOp = "CREATE_TRIGGER"; break;
|
||
+ case SQLITE_CREATE_VIEW: zOp = "CREATE_VIEW"; break;
|
||
+ case SQLITE_DELETE: zOp = "DELETE"; break;
|
||
+ case SQLITE_DROP_INDEX: zOp = "DROP_INDEX"; break;
|
||
+ case SQLITE_DROP_TABLE: zOp = "DROP_TABLE"; break;
|
||
+ case SQLITE_DROP_TEMP_INDEX: zOp = "DROP_TEMP_INDEX"; break;
|
||
+ case SQLITE_DROP_TEMP_TABLE: zOp = "DROP_TEMP_TABLE"; break;
|
||
+ case SQLITE_DROP_TEMP_TRIGGER: zOp = "DROP_TEMP_TRIGGER"; break;
|
||
+ case SQLITE_DROP_TEMP_VIEW: zOp = "DROP_TEMP_VIEW"; break;
|
||
+ case SQLITE_DROP_TRIGGER: zOp = "DROP_TRIGGER"; break;
|
||
+ case SQLITE_DROP_VIEW: zOp = "DROP_VIEW"; break;
|
||
+ case SQLITE_INSERT: zOp = "INSERT"; break;
|
||
+ case SQLITE_PRAGMA: zOp = "PRAGMA"; break;
|
||
+ case SQLITE_READ: zOp = "READ"; break;
|
||
+ case SQLITE_SELECT: zOp = "SELECT"; break;
|
||
+ case SQLITE_TRANSACTION: zOp = "TRANSACTION"; break;
|
||
+ case SQLITE_UPDATE: zOp = "UPDATE"; break;
|
||
+ case SQLITE_ATTACH: zOp = "ATTACH"; break;
|
||
+ case SQLITE_DETACH: zOp = "DETACH"; break;
|
||
+ case SQLITE_ALTER_TABLE: zOp = "ALTER_TABLE"; break;
|
||
+ case SQLITE_REINDEX: zOp = "REINDEX"; break;
|
||
+ case SQLITE_ANALYZE: zOp = "ANALYZE"; break;
|
||
+ case SQLITE_CREATE_VTABLE: zOp = "CREATE_VTABLE"; break;
|
||
+ case SQLITE_DROP_VTABLE: zOp = "DROP_VTABLE"; break;
|
||
+ case SQLITE_FUNCTION: zOp = "FUNCTION"; break;
|
||
+ case SQLITE_SAVEPOINT: zOp = "SAVEPOINT"; break;
|
||
+ case SQLITE_COPY: zOp = "COPY"; break;
|
||
+ case SQLITE_RECURSIVE: zOp = "RECURSIVE"; break;
|
||
+
|
||
+
|
||
+ default: {
|
||
+ sqlite3_snprintf(sizeof(zOpSpace), zOpSpace, "%d", op);
|
||
+ zOp = zOpSpace;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if( z1==0 ) z1 = "NULL";
|
||
+ if( z2==0 ) z2 = "NULL";
|
||
+ if( z3==0 ) z3 = "NULL";
|
||
+ if( z4==0 ) z4 = "NULL";
|
||
+ printf("AUTH: %s,%s,%s,%s,%s\n", zOp, z1, z2, z3, z4);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_showauth_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_set_authorizer(db, authCallback, 0);
|
||
+ return rc;
|
||
+}
|
||
--- origsrc/sqlite-autoconf-3080802/spellfix.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/spellfix.c 2015-01-31 00:31:56.490148500 +0100
|
||
@@ -0,0 +1,2889 @@
|
||
+/*
|
||
+** 2012 April 10
|
||
+**
|
||
+** 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 module implements the spellfix1 VIRTUAL TABLE that can be used
|
||
+** to search a large vocabulary for close matches. See separate
|
||
+** documentation (http://www.sqlite.org/spellfix1.html) for details.
|
||
+*/
|
||
+#include <string.h>
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+
|
||
+#ifndef SQLITE_AMALGAMATION
|
||
+# include <stdio.h>
|
||
+# include <stdlib.h>
|
||
+# include <assert.h>
|
||
+# define ALWAYS(X) 1
|
||
+# define NEVER(X) 0
|
||
+ typedef unsigned char u8;
|
||
+ typedef unsigned short u16;
|
||
+#endif
|
||
+#include <ctype.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+/*
|
||
+** Character classes for ASCII characters:
|
||
+**
|
||
+** 0 '' Silent letters: H W
|
||
+** 1 'A' Any vowel: A E I O U (Y)
|
||
+** 2 'B' A bilabeal stop or fricative: B F P V W
|
||
+** 3 'C' Other fricatives or back stops: C G J K Q S X Z
|
||
+** 4 'D' Alveolar stops: D T
|
||
+** 5 'H' Letter H at the beginning of a word
|
||
+** 6 'L' Glide: L
|
||
+** 7 'R' Semivowel: R
|
||
+** 8 'M' Nasals: M N
|
||
+** 9 'Y' Letter Y at the beginning of a word.
|
||
+** 10 '9' Digits: 0 1 2 3 4 5 6 7 8 9
|
||
+** 11 ' ' White space
|
||
+** 12 '?' Other.
|
||
+*/
|
||
+#define CCLASS_SILENT 0
|
||
+#define CCLASS_VOWEL 1
|
||
+#define CCLASS_B 2
|
||
+#define CCLASS_C 3
|
||
+#define CCLASS_D 4
|
||
+#define CCLASS_H 5
|
||
+#define CCLASS_L 6
|
||
+#define CCLASS_R 7
|
||
+#define CCLASS_M 8
|
||
+#define CCLASS_Y 9
|
||
+#define CCLASS_DIGIT 10
|
||
+#define CCLASS_SPACE 11
|
||
+#define CCLASS_OTHER 12
|
||
+
|
||
+/*
|
||
+** The following table gives the character class for non-initial ASCII
|
||
+** characters.
|
||
+*/
|
||
+static const unsigned char midClass[] = {
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE,
|
||
+ /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER,
|
||
+ /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER,
|
||
+ /* ' */ CCLASS_SILENT, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER,
|
||
+ /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER,
|
||
+ /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER,
|
||
+ /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT,
|
||
+ /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT,
|
||
+ /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT,
|
||
+ /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER,
|
||
+ /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER,
|
||
+ /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL,
|
||
+ /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D,
|
||
+ /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C,
|
||
+ /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C,
|
||
+ /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M,
|
||
+ /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B,
|
||
+ /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C,
|
||
+ /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B,
|
||
+ /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_VOWEL,
|
||
+ /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER,
|
||
+ /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER,
|
||
+ /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B,
|
||
+ /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL,
|
||
+ /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT,
|
||
+ /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C,
|
||
+ /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M,
|
||
+ /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C,
|
||
+ /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D,
|
||
+ /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B,
|
||
+ /* x */ CCLASS_C, /* y */ CCLASS_VOWEL, /* z */ CCLASS_C,
|
||
+ /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER,
|
||
+ /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+};
|
||
+/*
|
||
+** This tables gives the character class for ASCII characters that form the
|
||
+** initial character of a word. The only difference from midClass is with
|
||
+** the letters H, W, and Y.
|
||
+*/
|
||
+static const unsigned char initClass[] = {
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+ /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE,
|
||
+ /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER,
|
||
+ /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER,
|
||
+ /* ' */ CCLASS_OTHER, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER,
|
||
+ /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER,
|
||
+ /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER,
|
||
+ /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT,
|
||
+ /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT,
|
||
+ /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT,
|
||
+ /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER,
|
||
+ /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER,
|
||
+ /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL,
|
||
+ /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D,
|
||
+ /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C,
|
||
+ /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C,
|
||
+ /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M,
|
||
+ /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B,
|
||
+ /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C,
|
||
+ /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B,
|
||
+ /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_Y,
|
||
+ /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER,
|
||
+ /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER,
|
||
+ /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B,
|
||
+ /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL,
|
||
+ /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT,
|
||
+ /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C,
|
||
+ /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M,
|
||
+ /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C,
|
||
+ /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D,
|
||
+ /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B,
|
||
+ /* x */ CCLASS_C, /* y */ CCLASS_Y, /* z */ CCLASS_C,
|
||
+ /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER,
|
||
+ /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER,
|
||
+};
|
||
+
|
||
+/*
|
||
+** Mapping from the character class number (0-13) to a symbol for each
|
||
+** character class. Note that initClass[] can be used to map the class
|
||
+** symbol back into the class number.
|
||
+*/
|
||
+static const unsigned char className[] = ".ABCDHLRMY9 ?";
|
||
+
|
||
+/*
|
||
+** Generate a "phonetic hash" from a string of ASCII characters
|
||
+** in zIn[0..nIn-1].
|
||
+**
|
||
+** * Map characters by character class as defined above.
|
||
+** * Omit double-letters
|
||
+** * Omit vowels beside R and L
|
||
+** * Omit T when followed by CH
|
||
+** * Omit W when followed by R
|
||
+** * Omit D when followed by J or G
|
||
+** * Omit K in KN or G in GN at the beginning of a word
|
||
+**
|
||
+** Space to hold the result is obtained from sqlite3_malloc()
|
||
+**
|
||
+** Return NULL if memory allocation fails.
|
||
+*/
|
||
+static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){
|
||
+ unsigned char *zOut = sqlite3_malloc( nIn + 1 );
|
||
+ int i;
|
||
+ int nOut = 0;
|
||
+ char cPrev = 0x77;
|
||
+ char cPrevX = 0x77;
|
||
+ const unsigned char *aClass = initClass;
|
||
+
|
||
+ if( zOut==0 ) return 0;
|
||
+ if( nIn>2 ){
|
||
+ switch( zIn[0] ){
|
||
+ case 'g':
|
||
+ case 'k': {
|
||
+ if( zIn[1]=='n' ){ zIn++; nIn--; }
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ for(i=0; i<nIn; i++){
|
||
+ unsigned char c = zIn[i];
|
||
+ if( i+1<nIn ){
|
||
+ if( c=='w' && zIn[i+1]=='r' ) continue;
|
||
+ if( c=='d' && (zIn[i+1]=='j' || zIn[i+1]=='g') ) continue;
|
||
+ if( i+2<nIn ){
|
||
+ if( c=='t' && zIn[i+1]=='c' && zIn[i+2]=='h' ) continue;
|
||
+ }
|
||
+ }
|
||
+ c = aClass[c&0x7f];
|
||
+ if( c==CCLASS_SPACE ) continue;
|
||
+ if( c==CCLASS_OTHER && cPrev!=CCLASS_DIGIT ) continue;
|
||
+ aClass = midClass;
|
||
+ if( c==CCLASS_VOWEL && (cPrevX==CCLASS_R || cPrevX==CCLASS_L) ){
|
||
+ continue; /* No vowels beside L or R */
|
||
+ }
|
||
+ if( (c==CCLASS_R || c==CCLASS_L) && cPrevX==CCLASS_VOWEL ){
|
||
+ nOut--; /* No vowels beside L or R */
|
||
+ }
|
||
+ cPrev = c;
|
||
+ if( c==CCLASS_SILENT ) continue;
|
||
+ cPrevX = c;
|
||
+ c = className[c];
|
||
+ assert( nOut>=0 );
|
||
+ if( nOut==0 || c!=zOut[nOut-1] ) zOut[nOut++] = c;
|
||
+ }
|
||
+ zOut[nOut] = 0;
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This is an SQL function wrapper around phoneticHash(). See
|
||
+** the description of phoneticHash() for additional information.
|
||
+*/
|
||
+static void phoneticHashSqlFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zIn;
|
||
+ unsigned char *zOut;
|
||
+
|
||
+ zIn = sqlite3_value_text(argv[0]);
|
||
+ if( zIn==0 ) return;
|
||
+ zOut = phoneticHash(zIn, sqlite3_value_bytes(argv[0]));
|
||
+ if( zOut==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else{
|
||
+ sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the character class number for a character given its
|
||
+** context.
|
||
+*/
|
||
+static char characterClass(char cPrev, char c){
|
||
+ return cPrev==0 ? initClass[c&0x7f] : midClass[c&0x7f];
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the cost of inserting or deleting character c immediately
|
||
+** following character cPrev. If cPrev==0, that means c is the first
|
||
+** character of the word.
|
||
+*/
|
||
+static int insertOrDeleteCost(char cPrev, char c, char cNext){
|
||
+ char classC = characterClass(cPrev, c);
|
||
+ char classCprev;
|
||
+
|
||
+ if( classC==CCLASS_SILENT ){
|
||
+ /* Insert or delete "silent" characters such as H or W */
|
||
+ return 1;
|
||
+ }
|
||
+ if( cPrev==c ){
|
||
+ /* Repeated characters, or miss a repeat */
|
||
+ return 10;
|
||
+ }
|
||
+ if( classC==CCLASS_VOWEL && (cPrev=='r' || cNext=='r') ){
|
||
+ return 20; /* Insert a vowel before or after 'r' */
|
||
+ }
|
||
+ classCprev = characterClass(cPrev, cPrev);
|
||
+ if( classC==classCprev ){
|
||
+ if( classC==CCLASS_VOWEL ){
|
||
+ /* Remove or add a new vowel to a vowel cluster */
|
||
+ return 15;
|
||
+ }else{
|
||
+ /* Remove or add a consonant not in the same class */
|
||
+ return 50;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* any other character insertion or deletion */
|
||
+ return 100;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Divide the insertion cost by this factor when appending to the
|
||
+** end of the word.
|
||
+*/
|
||
+#define FINAL_INS_COST_DIV 4
|
||
+
|
||
+/*
|
||
+** Return the cost of substituting cTo in place of cFrom assuming
|
||
+** the previous character is cPrev. If cPrev==0 then cTo is the first
|
||
+** character of the word.
|
||
+*/
|
||
+static int substituteCost(char cPrev, char cFrom, char cTo){
|
||
+ char classFrom, classTo;
|
||
+ if( cFrom==cTo ){
|
||
+ /* Exact match */
|
||
+ return 0;
|
||
+ }
|
||
+ if( cFrom==(cTo^0x20) && ((cTo>='A' && cTo<='Z') || (cTo>='a' && cTo<='z')) ){
|
||
+ /* differ only in case */
|
||
+ return 0;
|
||
+ }
|
||
+ classFrom = characterClass(cPrev, cFrom);
|
||
+ classTo = characterClass(cPrev, cTo);
|
||
+ if( classFrom==classTo ){
|
||
+ /* Same character class */
|
||
+ return 40;
|
||
+ }
|
||
+ if( classFrom>=CCLASS_B && classFrom<=CCLASS_Y
|
||
+ && classTo>=CCLASS_B && classTo<=CCLASS_Y ){
|
||
+ /* Convert from one consonant to another, but in a different class */
|
||
+ return 75;
|
||
+ }
|
||
+ /* Any other subsitution */
|
||
+ return 100;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Given two strings zA and zB which are pure ASCII, return the cost
|
||
+** of transforming zA into zB. If zA ends with '*' assume that it is
|
||
+** a prefix of zB and give only minimal penalty for extra characters
|
||
+** on the end of zB.
|
||
+**
|
||
+** Smaller numbers mean a closer match.
|
||
+**
|
||
+** Negative values indicate an error:
|
||
+** -1 One of the inputs is NULL
|
||
+** -2 Non-ASCII characters on input
|
||
+** -3 Unable to allocate memory
|
||
+**
|
||
+** If pnMatch is not NULL, then *pnMatch is set to the number of bytes
|
||
+** of zB that matched the pattern in zA. If zA does not end with a '*',
|
||
+** then this value is always the number of bytes in zB (i.e. strlen(zB)).
|
||
+** If zA does end in a '*', then it is the number of bytes in the prefix
|
||
+** of zB that was deemed to match zA.
|
||
+*/
|
||
+static int editdist1(const char *zA, const char *zB, int *pnMatch){
|
||
+ int nA, nB; /* Number of characters in zA[] and zB[] */
|
||
+ int xA, xB; /* Loop counters for zA[] and zB[] */
|
||
+ char cA, cB; /* Current character of zA and zB */
|
||
+ char cAprev, cBprev; /* Previous character of zA and zB */
|
||
+ char cAnext, cBnext; /* Next character in zA and zB */
|
||
+ int d; /* North-west cost value */
|
||
+ int dc = 0; /* North-west character value */
|
||
+ int res; /* Final result */
|
||
+ int *m; /* The cost matrix */
|
||
+ char *cx; /* Corresponding character values */
|
||
+ int *toFree = 0; /* Malloced space */
|
||
+ int mStack[60+15]; /* Stack space to use if not too much is needed */
|
||
+ int nMatch = 0;
|
||
+
|
||
+ /* Early out if either input is NULL */
|
||
+ if( zA==0 || zB==0 ) return -1;
|
||
+
|
||
+ /* Skip any common prefix */
|
||
+ while( zA[0] && zA[0]==zB[0] ){ dc = zA[0]; zA++; zB++; nMatch++; }
|
||
+ if( pnMatch ) *pnMatch = nMatch;
|
||
+ if( zA[0]==0 && zB[0]==0 ) return 0;
|
||
+
|
||
+#if 0
|
||
+ printf("A=\"%s\" B=\"%s\" dc=%c\n", zA, zB, dc?dc:' ');
|
||
+#endif
|
||
+
|
||
+ /* Verify input strings and measure their lengths */
|
||
+ for(nA=0; zA[nA]; nA++){
|
||
+ if( zA[nA]&0x80 ) return -2;
|
||
+ }
|
||
+ for(nB=0; zB[nB]; nB++){
|
||
+ if( zB[nB]&0x80 ) return -2;
|
||
+ }
|
||
+
|
||
+ /* Special processing if either string is empty */
|
||
+ if( nA==0 ){
|
||
+ cBprev = dc;
|
||
+ for(xB=res=0; (cB = zB[xB])!=0; xB++){
|
||
+ res += insertOrDeleteCost(cBprev, cB, zB[xB+1])/FINAL_INS_COST_DIV;
|
||
+ cBprev = cB;
|
||
+ }
|
||
+ return res;
|
||
+ }
|
||
+ if( nB==0 ){
|
||
+ cAprev = dc;
|
||
+ for(xA=res=0; (cA = zA[xA])!=0; xA++){
|
||
+ res += insertOrDeleteCost(cAprev, cA, zA[xA+1]);
|
||
+ cAprev = cA;
|
||
+ }
|
||
+ return res;
|
||
+ }
|
||
+
|
||
+ /* A is a prefix of B */
|
||
+ if( zA[0]=='*' && zA[1]==0 ) return 0;
|
||
+
|
||
+ /* Allocate and initialize the Wagner matrix */
|
||
+ if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){
|
||
+ m = mStack;
|
||
+ }else{
|
||
+ m = toFree = sqlite3_malloc( (nB+1)*5*sizeof(m[0])/4 );
|
||
+ if( m==0 ) return -3;
|
||
+ }
|
||
+ cx = (char*)&m[nB+1];
|
||
+
|
||
+ /* Compute the Wagner edit distance */
|
||
+ m[0] = 0;
|
||
+ cx[0] = dc;
|
||
+ cBprev = dc;
|
||
+ for(xB=1; xB<=nB; xB++){
|
||
+ cBnext = zB[xB];
|
||
+ cB = zB[xB-1];
|
||
+ cx[xB] = cB;
|
||
+ m[xB] = m[xB-1] + insertOrDeleteCost(cBprev, cB, cBnext);
|
||
+ cBprev = cB;
|
||
+ }
|
||
+ cAprev = dc;
|
||
+ for(xA=1; xA<=nA; xA++){
|
||
+ int lastA = (xA==nA);
|
||
+ cA = zA[xA-1];
|
||
+ cAnext = zA[xA];
|
||
+ if( cA=='*' && lastA ) break;
|
||
+ d = m[0];
|
||
+ dc = cx[0];
|
||
+ m[0] = d + insertOrDeleteCost(cAprev, cA, cAnext);
|
||
+ cBprev = 0;
|
||
+ for(xB=1; xB<=nB; xB++){
|
||
+ int totalCost, insCost, delCost, subCost, ncx;
|
||
+ cB = zB[xB-1];
|
||
+ cBnext = zB[xB];
|
||
+
|
||
+ /* Cost to insert cB */
|
||
+ insCost = insertOrDeleteCost(cx[xB-1], cB, cBnext);
|
||
+ if( lastA ) insCost /= FINAL_INS_COST_DIV;
|
||
+
|
||
+ /* Cost to delete cA */
|
||
+ delCost = insertOrDeleteCost(cx[xB], cA, cBnext);
|
||
+
|
||
+ /* Cost to substitute cA->cB */
|
||
+ subCost = substituteCost(cx[xB-1], cA, cB);
|
||
+
|
||
+ /* Best cost */
|
||
+ totalCost = insCost + m[xB-1];
|
||
+ ncx = cB;
|
||
+ if( (delCost + m[xB])<totalCost ){
|
||
+ totalCost = delCost + m[xB];
|
||
+ ncx = cA;
|
||
+ }
|
||
+ if( (subCost + d)<totalCost ){
|
||
+ totalCost = subCost + d;
|
||
+ }
|
||
+
|
||
+#if 0
|
||
+ printf("%d,%d d=%4d u=%4d r=%4d dc=%c cA=%c cB=%c"
|
||
+ " ins=%4d del=%4d sub=%4d t=%4d ncx=%c\n",
|
||
+ xA, xB, d, m[xB], m[xB-1], dc?dc:' ', cA, cB,
|
||
+ insCost, delCost, subCost, totalCost, ncx?ncx:' ');
|
||
+#endif
|
||
+
|
||
+ /* Update the matrix */
|
||
+ d = m[xB];
|
||
+ dc = cx[xB];
|
||
+ m[xB] = totalCost;
|
||
+ cx[xB] = ncx;
|
||
+ cBprev = cB;
|
||
+ }
|
||
+ cAprev = cA;
|
||
+ }
|
||
+
|
||
+ /* Free the wagner matrix and return the result */
|
||
+ if( cA=='*' ){
|
||
+ res = m[1];
|
||
+ for(xB=1; xB<=nB; xB++){
|
||
+ if( m[xB]<res ){
|
||
+ res = m[xB];
|
||
+ if( pnMatch ) *pnMatch = xB+nMatch;
|
||
+ }
|
||
+ }
|
||
+ }else{
|
||
+ res = m[nB];
|
||
+ /* In the current implementation, pnMatch is always NULL if zA does
|
||
+ ** not end in "*" */
|
||
+ assert( pnMatch==0 );
|
||
+ }
|
||
+ sqlite3_free(toFree);
|
||
+ return res;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Function: editdist(A,B)
|
||
+**
|
||
+** Return the cost of transforming string A into string B. Both strings
|
||
+** must be pure ASCII text. If A ends with '*' then it is assumed to be
|
||
+** a prefix of B and extra characters on the end of B have minimal additional
|
||
+** cost.
|
||
+*/
|
||
+static void editdistSqlFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ int res = editdist1(
|
||
+ (const char*)sqlite3_value_text(argv[0]),
|
||
+ (const char*)sqlite3_value_text(argv[1]),
|
||
+ 0);
|
||
+ if( res<0 ){
|
||
+ if( res==(-3) ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else if( res==(-2) ){
|
||
+ sqlite3_result_error(context, "non-ASCII input to editdist()", -1);
|
||
+ }else{
|
||
+ sqlite3_result_error(context, "NULL input to editdist()", -1);
|
||
+ }
|
||
+ }else{
|
||
+ sqlite3_result_int(context, res);
|
||
+ }
|
||
+}
|
||
+
|
||
+/* End of the fixed-cost edit distance implementation
|
||
+******************************************************************************
|
||
+*****************************************************************************
|
||
+** Begin: Configurable cost unicode edit distance routines
|
||
+*/
|
||
+/* Forward declaration of structures */
|
||
+typedef struct EditDist3Cost EditDist3Cost;
|
||
+typedef struct EditDist3Config EditDist3Config;
|
||
+typedef struct EditDist3Point EditDist3Point;
|
||
+typedef struct EditDist3From EditDist3From;
|
||
+typedef struct EditDist3FromString EditDist3FromString;
|
||
+typedef struct EditDist3To EditDist3To;
|
||
+typedef struct EditDist3ToString EditDist3ToString;
|
||
+typedef struct EditDist3Lang EditDist3Lang;
|
||
+
|
||
+
|
||
+/*
|
||
+** An entry in the edit cost table
|
||
+*/
|
||
+struct EditDist3Cost {
|
||
+ EditDist3Cost *pNext; /* Next cost element */
|
||
+ u8 nFrom; /* Number of bytes in aFrom */
|
||
+ u8 nTo; /* Number of bytes in aTo */
|
||
+ u16 iCost; /* Cost of this transformation */
|
||
+ char a[4] ; /* FROM string followed by TO string */
|
||
+ /* Additional TO and FROM string bytes appended as necessary */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Edit costs for a particular language ID
|
||
+*/
|
||
+struct EditDist3Lang {
|
||
+ int iLang; /* Language ID */
|
||
+ int iInsCost; /* Default insertion cost */
|
||
+ int iDelCost; /* Default deletion cost */
|
||
+ int iSubCost; /* Default substitution cost */
|
||
+ EditDist3Cost *pCost; /* Costs */
|
||
+};
|
||
+
|
||
+
|
||
+/*
|
||
+** The default EditDist3Lang object, with default costs.
|
||
+*/
|
||
+static const EditDist3Lang editDist3Lang = { 0, 100, 100, 150, 0 };
|
||
+
|
||
+/*
|
||
+** Complete configuration
|
||
+*/
|
||
+struct EditDist3Config {
|
||
+ int nLang; /* Number of language IDs. Size of a[] */
|
||
+ EditDist3Lang *a; /* One for each distinct language ID */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Extra information about each character in the FROM string.
|
||
+*/
|
||
+struct EditDist3From {
|
||
+ int nSubst; /* Number of substitution cost entries */
|
||
+ int nDel; /* Number of deletion cost entries */
|
||
+ int nByte; /* Number of bytes in this character */
|
||
+ EditDist3Cost **apSubst; /* Array of substitution costs for this element */
|
||
+ EditDist3Cost **apDel; /* Array of deletion cost entries */
|
||
+};
|
||
+
|
||
+/*
|
||
+** A precompiled FROM string.
|
||
+*
|
||
+** In the common case we expect the FROM string to be reused multiple times.
|
||
+** In other words, the common case will be to measure the edit distance
|
||
+** from a single origin string to multiple target strings.
|
||
+*/
|
||
+struct EditDist3FromString {
|
||
+ char *z; /* The complete text of the FROM string */
|
||
+ int n; /* Number of characters in the FROM string */
|
||
+ int isPrefix; /* True if ends with '*' character */
|
||
+ EditDist3From *a; /* Extra info about each char of the FROM string */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Extra information about each character in the TO string.
|
||
+*/
|
||
+struct EditDist3To {
|
||
+ int nIns; /* Number of insertion cost entries */
|
||
+ int nByte; /* Number of bytes in this character */
|
||
+ EditDist3Cost **apIns; /* Array of deletion cost entries */
|
||
+};
|
||
+
|
||
+/*
|
||
+** A precompiled FROM string
|
||
+*/
|
||
+struct EditDist3ToString {
|
||
+ char *z; /* The complete text of the TO string */
|
||
+ int n; /* Number of characters in the TO string */
|
||
+ EditDist3To *a; /* Extra info about each char of the TO string */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Clear or delete an instance of the object that records all edit-distance
|
||
+** weights.
|
||
+*/
|
||
+static void editDist3ConfigClear(EditDist3Config *p){
|
||
+ int i;
|
||
+ if( p==0 ) return;
|
||
+ for(i=0; i<p->nLang; i++){
|
||
+ EditDist3Cost *pCost, *pNext;
|
||
+ pCost = p->a[i].pCost;
|
||
+ while( pCost ){
|
||
+ pNext = pCost->pNext;
|
||
+ sqlite3_free(pCost);
|
||
+ pCost = pNext;
|
||
+ }
|
||
+ }
|
||
+ sqlite3_free(p->a);
|
||
+ memset(p, 0, sizeof(*p));
|
||
+}
|
||
+static void editDist3ConfigDelete(void *pIn){
|
||
+ EditDist3Config *p = (EditDist3Config*)pIn;
|
||
+ editDist3ConfigClear(p);
|
||
+ sqlite3_free(p);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Load all edit-distance weights from a table.
|
||
+*/
|
||
+static int editDist3ConfigLoad(
|
||
+ EditDist3Config *p, /* The edit distance configuration to load */
|
||
+ sqlite3 *db, /* Load from this database */
|
||
+ const char *zTable /* Name of the table from which to load */
|
||
+){
|
||
+ sqlite3_stmt *pStmt;
|
||
+ int rc, rc2;
|
||
+ char *zSql;
|
||
+ int iLangPrev = -9999;
|
||
+ EditDist3Lang *pLang = 0;
|
||
+
|
||
+ zSql = sqlite3_mprintf("SELECT iLang, cFrom, cTo, iCost"
|
||
+ " FROM \"%w\" WHERE iLang>=0 ORDER BY iLang", zTable);
|
||
+ if( zSql==0 ) return SQLITE_NOMEM;
|
||
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ if( rc ) return rc;
|
||
+ editDist3ConfigClear(p);
|
||
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||
+ int iLang = sqlite3_column_int(pStmt, 0);
|
||
+ const char *zFrom = (const char*)sqlite3_column_text(pStmt, 1);
|
||
+ int nFrom = zFrom ? sqlite3_column_bytes(pStmt, 1) : 0;
|
||
+ const char *zTo = (const char*)sqlite3_column_text(pStmt, 2);
|
||
+ int nTo = zTo ? sqlite3_column_bytes(pStmt, 2) : 0;
|
||
+ int iCost = sqlite3_column_int(pStmt, 3);
|
||
+
|
||
+ assert( zFrom!=0 || nFrom==0 );
|
||
+ assert( zTo!=0 || nTo==0 );
|
||
+ if( nFrom>100 || nTo>100 ) continue;
|
||
+ if( iCost<0 ) continue;
|
||
+ if( pLang==0 || iLang!=iLangPrev ){
|
||
+ EditDist3Lang *pNew;
|
||
+ pNew = sqlite3_realloc(p->a, (p->nLang+1)*sizeof(p->a[0]));
|
||
+ if( pNew==0 ){ rc = SQLITE_NOMEM; break; }
|
||
+ p->a = pNew;
|
||
+ pLang = &p->a[p->nLang];
|
||
+ p->nLang++;
|
||
+ pLang->iLang = iLang;
|
||
+ pLang->iInsCost = 100;
|
||
+ pLang->iDelCost = 100;
|
||
+ pLang->iSubCost = 150;
|
||
+ pLang->pCost = 0;
|
||
+ iLangPrev = iLang;
|
||
+ }
|
||
+ if( nFrom==1 && zFrom[0]=='?' && nTo==0 ){
|
||
+ pLang->iDelCost = iCost;
|
||
+ }else if( nFrom==0 && nTo==1 && zTo[0]=='?' ){
|
||
+ pLang->iInsCost = iCost;
|
||
+ }else if( nFrom==1 && nTo==1 && zFrom[0]=='?' && zTo[0]=='?' ){
|
||
+ pLang->iSubCost = iCost;
|
||
+ }else{
|
||
+ EditDist3Cost *pCost;
|
||
+ int nExtra = nFrom + nTo - 4;
|
||
+ if( nExtra<0 ) nExtra = 0;
|
||
+ pCost = sqlite3_malloc( sizeof(*pCost) + nExtra );
|
||
+ if( pCost==0 ){ rc = SQLITE_NOMEM; break; }
|
||
+ pCost->nFrom = nFrom;
|
||
+ pCost->nTo = nTo;
|
||
+ pCost->iCost = iCost;
|
||
+ memcpy(pCost->a, zFrom, nFrom);
|
||
+ memcpy(pCost->a + nFrom, zTo, nTo);
|
||
+ pCost->pNext = pLang->pCost;
|
||
+ pLang->pCost = pCost;
|
||
+ }
|
||
+ }
|
||
+ rc2 = sqlite3_finalize(pStmt);
|
||
+ if( rc==SQLITE_OK ) rc = rc2;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the length (in bytes) of a utf-8 character. Or return a maximum
|
||
+** of N.
|
||
+*/
|
||
+static int utf8Len(unsigned char c, int N){
|
||
+ int len = 1;
|
||
+ if( c>0x7f ){
|
||
+ if( (c&0xe0)==0xc0 ){
|
||
+ len = 2;
|
||
+ }else if( (c&0xf0)==0xe0 ){
|
||
+ len = 3;
|
||
+ }else{
|
||
+ len = 4;
|
||
+ }
|
||
+ }
|
||
+ if( len>N ) len = N;
|
||
+ return len;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE (non-zero) if the To side of the given cost matches
|
||
+** the given string.
|
||
+*/
|
||
+static int matchTo(EditDist3Cost *p, const char *z, int n){
|
||
+ if( p->nTo>n ) return 0;
|
||
+ if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0;
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE (non-zero) if the From side of the given cost matches
|
||
+** the given string.
|
||
+*/
|
||
+static int matchFrom(EditDist3Cost *p, const char *z, int n){
|
||
+ assert( p->nFrom<=n );
|
||
+ if( strncmp(p->a, z, p->nFrom)!=0 ) return 0;
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE (non-zero) of the next FROM character and the next TO
|
||
+** character are the same.
|
||
+*/
|
||
+static int matchFromTo(
|
||
+ EditDist3FromString *pStr, /* Left hand string */
|
||
+ int n1, /* Index of comparison character on the left */
|
||
+ const char *z2, /* Right-handl comparison character */
|
||
+ int n2 /* Bytes remaining in z2[] */
|
||
+){
|
||
+ int b1 = pStr->a[n1].nByte;
|
||
+ if( b1>n2 ) return 0;
|
||
+ if( memcmp(pStr->z+n1, z2, b1)!=0 ) return 0;
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Delete an EditDist3FromString objecct
|
||
+*/
|
||
+static void editDist3FromStringDelete(EditDist3FromString *p){
|
||
+ int i;
|
||
+ if( p ){
|
||
+ for(i=0; i<p->n; i++){
|
||
+ sqlite3_free(p->a[i].apDel);
|
||
+ sqlite3_free(p->a[i].apSubst);
|
||
+ }
|
||
+ sqlite3_free(p);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Create a EditDist3FromString object.
|
||
+*/
|
||
+static EditDist3FromString *editDist3FromStringNew(
|
||
+ const EditDist3Lang *pLang,
|
||
+ const char *z,
|
||
+ int n
|
||
+){
|
||
+ EditDist3FromString *pStr;
|
||
+ EditDist3Cost *p;
|
||
+ int i;
|
||
+
|
||
+ if( z==0 ) return 0;
|
||
+ if( n<0 ) n = (int)strlen(z);
|
||
+ pStr = sqlite3_malloc( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 );
|
||
+ if( pStr==0 ) return 0;
|
||
+ pStr->a = (EditDist3From*)&pStr[1];
|
||
+ memset(pStr->a, 0, sizeof(pStr->a[0])*n);
|
||
+ pStr->n = n;
|
||
+ pStr->z = (char*)&pStr->a[n];
|
||
+ memcpy(pStr->z, z, n+1);
|
||
+ if( n && z[n-1]=='*' ){
|
||
+ pStr->isPrefix = 1;
|
||
+ n--;
|
||
+ pStr->n--;
|
||
+ pStr->z[n] = 0;
|
||
+ }else{
|
||
+ pStr->isPrefix = 0;
|
||
+ }
|
||
+
|
||
+ for(i=0; i<n; i++){
|
||
+ EditDist3From *pFrom = &pStr->a[i];
|
||
+ memset(pFrom, 0, sizeof(*pFrom));
|
||
+ pFrom->nByte = utf8Len((unsigned char)z[i], n-i);
|
||
+ for(p=pLang->pCost; p; p=p->pNext){
|
||
+ EditDist3Cost **apNew;
|
||
+ if( i+p->nFrom>n ) continue;
|
||
+ if( matchFrom(p, z+i, n-i)==0 ) continue;
|
||
+ if( p->nTo==0 ){
|
||
+ apNew = sqlite3_realloc(pFrom->apDel,
|
||
+ sizeof(*apNew)*(pFrom->nDel+1));
|
||
+ if( apNew==0 ) break;
|
||
+ pFrom->apDel = apNew;
|
||
+ apNew[pFrom->nDel++] = p;
|
||
+ }else{
|
||
+ apNew = sqlite3_realloc(pFrom->apSubst,
|
||
+ sizeof(*apNew)*(pFrom->nSubst+1));
|
||
+ if( apNew==0 ) break;
|
||
+ pFrom->apSubst = apNew;
|
||
+ apNew[pFrom->nSubst++] = p;
|
||
+ }
|
||
+ }
|
||
+ if( p ){
|
||
+ editDist3FromStringDelete(pStr);
|
||
+ pStr = 0;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ return pStr;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Update entry m[i] such that it is the minimum of its current value
|
||
+** and m[j]+iCost.
|
||
+**
|
||
+** If the iCost is 1,000,000 or greater, then consider the cost to be
|
||
+** infinite and skip the update.
|
||
+*/
|
||
+static void updateCost(
|
||
+ unsigned int *m,
|
||
+ int i,
|
||
+ int j,
|
||
+ int iCost
|
||
+){
|
||
+ assert( iCost>=0 );
|
||
+ if( iCost<10000 ){
|
||
+ unsigned int b = m[j] + iCost;
|
||
+ if( b<m[i] ) m[i] = b;
|
||
+ }
|
||
+}
|
||
+
|
||
+/* Compute the edit distance between two strings.
|
||
+**
|
||
+** If an error occurs, return a negative number which is the error code.
|
||
+**
|
||
+** If pnMatch is not NULL, then *pnMatch is set to the number of characters
|
||
+** (not bytes) in z2 that matched the search pattern in *pFrom. If pFrom does
|
||
+** not contain the pattern for a prefix-search, then this is always the number
|
||
+** of characters in z2. If pFrom does contain a prefix search pattern, then
|
||
+** it is the number of characters in the prefix of z2 that was deemed to
|
||
+** match pFrom.
|
||
+*/
|
||
+static int editDist3Core(
|
||
+ EditDist3FromString *pFrom, /* The FROM string */
|
||
+ const char *z2, /* The TO string */
|
||
+ int n2, /* Length of the TO string */
|
||
+ const EditDist3Lang *pLang, /* Edit weights for a particular language ID */
|
||
+ int *pnMatch /* OUT: Characters in matched prefix */
|
||
+){
|
||
+ int k, n;
|
||
+ int i1, b1;
|
||
+ int i2, b2;
|
||
+ EditDist3FromString f = *pFrom;
|
||
+ EditDist3To *a2;
|
||
+ unsigned int *m;
|
||
+ int szRow;
|
||
+ EditDist3Cost *p;
|
||
+ int res;
|
||
+
|
||
+ /* allocate the Wagner matrix and the aTo[] array for the TO string */
|
||
+ n = (f.n+1)*(n2+1);
|
||
+ n = (n+1)&~1;
|
||
+ m = sqlite3_malloc( n*sizeof(m[0]) + sizeof(a2[0])*n2 );
|
||
+ if( m==0 ) return -1; /* Out of memory */
|
||
+ a2 = (EditDist3To*)&m[n];
|
||
+ memset(a2, 0, sizeof(a2[0])*n2);
|
||
+
|
||
+ /* Fill in the a1[] matrix for all characters of the TO string */
|
||
+ for(i2=0; i2<n2; i2++){
|
||
+ a2[i2].nByte = utf8Len((unsigned char)z2[i2], n2-i2);
|
||
+ for(p=pLang->pCost; p; p=p->pNext){
|
||
+ EditDist3Cost **apNew;
|
||
+ if( p->nFrom>0 ) continue;
|
||
+ if( i2+p->nTo>n2 ) continue;
|
||
+ if( matchTo(p, z2+i2, n2-i2)==0 ) continue;
|
||
+ a2[i2].nIns++;
|
||
+ apNew = sqlite3_realloc(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns);
|
||
+ if( apNew==0 ){
|
||
+ res = -1; /* Out of memory */
|
||
+ goto editDist3Abort;
|
||
+ }
|
||
+ a2[i2].apIns = apNew;
|
||
+ a2[i2].apIns[a2[i2].nIns-1] = p;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Prepare to compute the minimum edit distance */
|
||
+ szRow = f.n+1;
|
||
+ memset(m, 0x01, (n2+1)*szRow*sizeof(m[0]));
|
||
+ m[0] = 0;
|
||
+
|
||
+ /* First fill in the top-row of the matrix with FROM deletion costs */
|
||
+ for(i1=0; i1<f.n; i1 += b1){
|
||
+ b1 = f.a[i1].nByte;
|
||
+ updateCost(m, i1+b1, i1, pLang->iDelCost);
|
||
+ for(k=0; k<f.a[i1].nDel; k++){
|
||
+ p = f.a[i1].apDel[k];
|
||
+ updateCost(m, i1+p->nFrom, i1, p->iCost);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Fill in all subsequent rows, top-to-bottom, left-to-right */
|
||
+ for(i2=0; i2<n2; i2 += b2){
|
||
+ int rx; /* Starting index for current row */
|
||
+ int rxp; /* Starting index for previous row */
|
||
+ b2 = a2[i2].nByte;
|
||
+ rx = szRow*(i2+b2);
|
||
+ rxp = szRow*i2;
|
||
+ updateCost(m, rx, rxp, pLang->iInsCost);
|
||
+ for(k=0; k<a2[i2].nIns; k++){
|
||
+ p = a2[i2].apIns[k];
|
||
+ updateCost(m, szRow*(i2+p->nTo), rxp, p->iCost);
|
||
+ }
|
||
+ for(i1=0; i1<f.n; i1+=b1){
|
||
+ int cx; /* Index of current cell */
|
||
+ int cxp; /* Index of cell immediately to the left */
|
||
+ int cxd; /* Index of cell to the left and one row above */
|
||
+ int cxu; /* Index of cell immediately above */
|
||
+ b1 = f.a[i1].nByte;
|
||
+ cxp = rx + i1;
|
||
+ cx = cxp + b1;
|
||
+ cxd = rxp + i1;
|
||
+ cxu = cxd + b1;
|
||
+ updateCost(m, cx, cxp, pLang->iDelCost);
|
||
+ for(k=0; k<f.a[i1].nDel; k++){
|
||
+ p = f.a[i1].apDel[k];
|
||
+ updateCost(m, cxp+p->nFrom, cxp, p->iCost);
|
||
+ }
|
||
+ updateCost(m, cx, cxu, pLang->iInsCost);
|
||
+ if( matchFromTo(&f, i1, z2+i2, n2-i2) ){
|
||
+ updateCost(m, cx, cxd, 0);
|
||
+ }
|
||
+ updateCost(m, cx, cxd, pLang->iSubCost);
|
||
+ for(k=0; k<f.a[i1].nSubst; k++){
|
||
+ p = f.a[i1].apSubst[k];
|
||
+ if( matchTo(p, z2+i2, n2-i2) ){
|
||
+ updateCost(m, cxd+p->nFrom+szRow*p->nTo, cxd, p->iCost);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+#if 0 /* Enable for debugging */
|
||
+ printf(" ^");
|
||
+ for(i1=0; i1<f.n; i1++) printf(" %c-%2x", f.z[i1], f.z[i1]&0xff);
|
||
+ printf("\n ^:");
|
||
+ for(i1=0; i1<szRow; i1++){
|
||
+ int v = m[i1];
|
||
+ if( v>9999 ) printf(" ****");
|
||
+ else printf(" %4d", v);
|
||
+ }
|
||
+ printf("\n");
|
||
+ for(i2=0; i2<n2; i2++){
|
||
+ printf("%c-%02x:", z2[i2], z2[i2]&0xff);
|
||
+ for(i1=0; i1<szRow; i1++){
|
||
+ int v = m[(i2+1)*szRow+i1];
|
||
+ if( v>9999 ) printf(" ****");
|
||
+ else printf(" %4d", v);
|
||
+ }
|
||
+ printf("\n");
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ /* Free memory allocations and return the result */
|
||
+ res = (int)m[szRow*(n2+1)-1];
|
||
+ n = n2;
|
||
+ if( f.isPrefix ){
|
||
+ for(i2=1; i2<=n2; i2++){
|
||
+ int b = m[szRow*i2-1];
|
||
+ if( b<=res ){
|
||
+ res = b;
|
||
+ n = i2 - 1;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ if( pnMatch ){
|
||
+ int nExtra = 0;
|
||
+ for(k=0; k<n; k++){
|
||
+ if( (z2[k] & 0xc0)==0x80 ) nExtra++;
|
||
+ }
|
||
+ *pnMatch = n - nExtra;
|
||
+ }
|
||
+
|
||
+editDist3Abort:
|
||
+ for(i2=0; i2<n2; i2++) sqlite3_free(a2[i2].apIns);
|
||
+ sqlite3_free(m);
|
||
+ return res;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Get an appropriate EditDist3Lang object.
|
||
+*/
|
||
+static const EditDist3Lang *editDist3FindLang(
|
||
+ EditDist3Config *pConfig,
|
||
+ int iLang
|
||
+){
|
||
+ int i;
|
||
+ for(i=0; i<pConfig->nLang; i++){
|
||
+ if( pConfig->a[i].iLang==iLang ) return &pConfig->a[i];
|
||
+ }
|
||
+ return &editDist3Lang;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Function: editdist3(A,B,iLang)
|
||
+** editdist3(tablename)
|
||
+**
|
||
+** Return the cost of transforming string A into string B using edit
|
||
+** weights for iLang.
|
||
+**
|
||
+** The second form loads edit weights into memory from a table.
|
||
+*/
|
||
+static void editDist3SqlFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ EditDist3Config *pConfig = (EditDist3Config*)sqlite3_user_data(context);
|
||
+ sqlite3 *db = sqlite3_context_db_handle(context);
|
||
+ int rc;
|
||
+ if( argc==1 ){
|
||
+ const char *zTable = (const char*)sqlite3_value_text(argv[0]);
|
||
+ rc = editDist3ConfigLoad(pConfig, db, zTable);
|
||
+ if( rc ) sqlite3_result_error_code(context, rc);
|
||
+ }else{
|
||
+ const char *zA = (const char*)sqlite3_value_text(argv[0]);
|
||
+ const char *zB = (const char*)sqlite3_value_text(argv[1]);
|
||
+ int nA = sqlite3_value_bytes(argv[0]);
|
||
+ int nB = sqlite3_value_bytes(argv[1]);
|
||
+ int iLang = argc==3 ? sqlite3_value_int(argv[2]) : 0;
|
||
+ const EditDist3Lang *pLang = editDist3FindLang(pConfig, iLang);
|
||
+ EditDist3FromString *pFrom;
|
||
+ int dist;
|
||
+
|
||
+ pFrom = editDist3FromStringNew(pLang, zA, nA);
|
||
+ if( pFrom==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ return;
|
||
+ }
|
||
+ dist = editDist3Core(pFrom, zB, nB, pLang, 0);
|
||
+ editDist3FromStringDelete(pFrom);
|
||
+ if( dist==(-1) ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else{
|
||
+ sqlite3_result_int(context, dist);
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Register the editDist3 function with SQLite
|
||
+*/
|
||
+static int editDist3Install(sqlite3 *db){
|
||
+ int rc;
|
||
+ EditDist3Config *pConfig = sqlite3_malloc( sizeof(*pConfig) );
|
||
+ if( pConfig==0 ) return SQLITE_NOMEM;
|
||
+ memset(pConfig, 0, sizeof(*pConfig));
|
||
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
||
+ 2, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
||
+ 3, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function_v2(db, "editdist3",
|
||
+ 1, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0,
|
||
+ editDist3ConfigDelete);
|
||
+ }else{
|
||
+ sqlite3_free(pConfig);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+/* End configurable cost unicode edit distance routines
|
||
+******************************************************************************
|
||
+******************************************************************************
|
||
+** Begin transliterate unicode-to-ascii implementation
|
||
+*/
|
||
+
|
||
+#if !SQLITE_AMALGAMATION
|
||
+/*
|
||
+** This lookup table is used to help decode the first byte of
|
||
+** a multi-byte UTF8 character.
|
||
+*/
|
||
+static const unsigned char sqlite3Utf8Trans1[] = {
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||
+};
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Return the value of the first UTF-8 character in the string.
|
||
+*/
|
||
+static int utf8Read(const unsigned char *z, int n, int *pSize){
|
||
+ int c, i;
|
||
+
|
||
+ /* All callers to this routine (in the current implementation)
|
||
+ ** always have n>0. */
|
||
+ if( NEVER(n==0) ){
|
||
+ c = i = 0;
|
||
+ }else{
|
||
+ c = z[0];
|
||
+ i = 1;
|
||
+ if( c>=0xc0 ){
|
||
+ c = sqlite3Utf8Trans1[c-0xc0];
|
||
+ while( i<n && (z[i] & 0xc0)==0x80 ){
|
||
+ c = (c<<6) + (0x3f & z[i++]);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ *pSize = i;
|
||
+ return c;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the number of characters in the utf-8 string in the nIn byte
|
||
+** buffer pointed to by zIn.
|
||
+*/
|
||
+static int utf8Charlen(const char *zIn, int nIn){
|
||
+ int i;
|
||
+ int nChar = 0;
|
||
+ for(i=0; i<nIn; nChar++){
|
||
+ int sz;
|
||
+ utf8Read((const unsigned char *)&zIn[i], nIn-i, &sz);
|
||
+ i += sz;
|
||
+ }
|
||
+ return nChar;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Table of translations from unicode characters into ASCII.
|
||
+*/
|
||
+static const struct {
|
||
+ unsigned short int cFrom;
|
||
+ unsigned char cTo0, cTo1;
|
||
+} translit[] = {
|
||
+ { 0x00A0, 0x20, 0x00 }, /* to */
|
||
+ { 0x00B5, 0x75, 0x00 }, /* µ to u */
|
||
+ { 0x00C0, 0x41, 0x00 }, /* À to A */
|
||
+ { 0x00C1, 0x41, 0x00 }, /* Á to A */
|
||
+ { 0x00C2, 0x41, 0x00 }, /* Â to A */
|
||
+ { 0x00C3, 0x41, 0x00 }, /* Ã to A */
|
||
+ { 0x00C4, 0x41, 0x65 }, /* Ä to Ae */
|
||
+ { 0x00C5, 0x41, 0x61 }, /* Å to Aa */
|
||
+ { 0x00C6, 0x41, 0x45 }, /* Æ to AE */
|
||
+ { 0x00C7, 0x43, 0x00 }, /* Ç to C */
|
||
+ { 0x00C8, 0x45, 0x00 }, /* È to E */
|
||
+ { 0x00C9, 0x45, 0x00 }, /* É to E */
|
||
+ { 0x00CA, 0x45, 0x00 }, /* Ê to E */
|
||
+ { 0x00CB, 0x45, 0x00 }, /* Ë to E */
|
||
+ { 0x00CC, 0x49, 0x00 }, /* Ì to I */
|
||
+ { 0x00CD, 0x49, 0x00 }, /* Í to I */
|
||
+ { 0x00CE, 0x49, 0x00 }, /* Î to I */
|
||
+ { 0x00CF, 0x49, 0x00 }, /* Ï to I */
|
||
+ { 0x00D0, 0x44, 0x00 }, /* Ð to D */
|
||
+ { 0x00D1, 0x4E, 0x00 }, /* Ñ to N */
|
||
+ { 0x00D2, 0x4F, 0x00 }, /* Ò to O */
|
||
+ { 0x00D3, 0x4F, 0x00 }, /* Ó to O */
|
||
+ { 0x00D4, 0x4F, 0x00 }, /* Ô to O */
|
||
+ { 0x00D5, 0x4F, 0x00 }, /* Õ to O */
|
||
+ { 0x00D6, 0x4F, 0x65 }, /* Ö to Oe */
|
||
+ { 0x00D7, 0x78, 0x00 }, /* × to x */
|
||
+ { 0x00D8, 0x4F, 0x00 }, /* Ø to O */
|
||
+ { 0x00D9, 0x55, 0x00 }, /* Ù to U */
|
||
+ { 0x00DA, 0x55, 0x00 }, /* Ú to U */
|
||
+ { 0x00DB, 0x55, 0x00 }, /* Û to U */
|
||
+ { 0x00DC, 0x55, 0x65 }, /* Ü to Ue */
|
||
+ { 0x00DD, 0x59, 0x00 }, /* Ý to Y */
|
||
+ { 0x00DE, 0x54, 0x68 }, /* Þ to Th */
|
||
+ { 0x00DF, 0x73, 0x73 }, /* ß to ss */
|
||
+ { 0x00E0, 0x61, 0x00 }, /* à to a */
|
||
+ { 0x00E1, 0x61, 0x00 }, /* á to a */
|
||
+ { 0x00E2, 0x61, 0x00 }, /* â to a */
|
||
+ { 0x00E3, 0x61, 0x00 }, /* ã to a */
|
||
+ { 0x00E4, 0x61, 0x65 }, /* ä to ae */
|
||
+ { 0x00E5, 0x61, 0x61 }, /* å to aa */
|
||
+ { 0x00E6, 0x61, 0x65 }, /* æ to ae */
|
||
+ { 0x00E7, 0x63, 0x00 }, /* ç to c */
|
||
+ { 0x00E8, 0x65, 0x00 }, /* è to e */
|
||
+ { 0x00E9, 0x65, 0x00 }, /* é to e */
|
||
+ { 0x00EA, 0x65, 0x00 }, /* ê to e */
|
||
+ { 0x00EB, 0x65, 0x00 }, /* ë to e */
|
||
+ { 0x00EC, 0x69, 0x00 }, /* ì to i */
|
||
+ { 0x00ED, 0x69, 0x00 }, /* í to i */
|
||
+ { 0x00EE, 0x69, 0x00 }, /* î to i */
|
||
+ { 0x00EF, 0x69, 0x00 }, /* ï to i */
|
||
+ { 0x00F0, 0x64, 0x00 }, /* ð to d */
|
||
+ { 0x00F1, 0x6E, 0x00 }, /* ñ to n */
|
||
+ { 0x00F2, 0x6F, 0x00 }, /* ò to o */
|
||
+ { 0x00F3, 0x6F, 0x00 }, /* ó to o */
|
||
+ { 0x00F4, 0x6F, 0x00 }, /* ô to o */
|
||
+ { 0x00F5, 0x6F, 0x00 }, /* õ to o */
|
||
+ { 0x00F6, 0x6F, 0x65 }, /* ö to oe */
|
||
+ { 0x00F7, 0x3A, 0x00 }, /* ÷ to : */
|
||
+ { 0x00F8, 0x6F, 0x00 }, /* ø to o */
|
||
+ { 0x00F9, 0x75, 0x00 }, /* ù to u */
|
||
+ { 0x00FA, 0x75, 0x00 }, /* ú to u */
|
||
+ { 0x00FB, 0x75, 0x00 }, /* û to u */
|
||
+ { 0x00FC, 0x75, 0x65 }, /* ü to ue */
|
||
+ { 0x00FD, 0x79, 0x00 }, /* ý to y */
|
||
+ { 0x00FE, 0x74, 0x68 }, /* þ to th */
|
||
+ { 0x00FF, 0x79, 0x00 }, /* ÿ to y */
|
||
+ { 0x0100, 0x41, 0x00 }, /* Ā to A */
|
||
+ { 0x0101, 0x61, 0x00 }, /* ā to a */
|
||
+ { 0x0102, 0x41, 0x00 }, /* Ă to A */
|
||
+ { 0x0103, 0x61, 0x00 }, /* ă to a */
|
||
+ { 0x0104, 0x41, 0x00 }, /* Ą to A */
|
||
+ { 0x0105, 0x61, 0x00 }, /* ą to a */
|
||
+ { 0x0106, 0x43, 0x00 }, /* Ć to C */
|
||
+ { 0x0107, 0x63, 0x00 }, /* ć to c */
|
||
+ { 0x0108, 0x43, 0x68 }, /* Ĉ to Ch */
|
||
+ { 0x0109, 0x63, 0x68 }, /* ĉ to ch */
|
||
+ { 0x010A, 0x43, 0x00 }, /* Ċ to C */
|
||
+ { 0x010B, 0x63, 0x00 }, /* ċ to c */
|
||
+ { 0x010C, 0x43, 0x00 }, /* Č to C */
|
||
+ { 0x010D, 0x63, 0x00 }, /* č to c */
|
||
+ { 0x010E, 0x44, 0x00 }, /* Ď to D */
|
||
+ { 0x010F, 0x64, 0x00 }, /* ď to d */
|
||
+ { 0x0110, 0x44, 0x00 }, /* Đ to D */
|
||
+ { 0x0111, 0x64, 0x00 }, /* đ to d */
|
||
+ { 0x0112, 0x45, 0x00 }, /* Ē to E */
|
||
+ { 0x0113, 0x65, 0x00 }, /* ē to e */
|
||
+ { 0x0114, 0x45, 0x00 }, /* Ĕ to E */
|
||
+ { 0x0115, 0x65, 0x00 }, /* ĕ to e */
|
||
+ { 0x0116, 0x45, 0x00 }, /* Ė to E */
|
||
+ { 0x0117, 0x65, 0x00 }, /* ė to e */
|
||
+ { 0x0118, 0x45, 0x00 }, /* Ę to E */
|
||
+ { 0x0119, 0x65, 0x00 }, /* ę to e */
|
||
+ { 0x011A, 0x45, 0x00 }, /* Ě to E */
|
||
+ { 0x011B, 0x65, 0x00 }, /* ě to e */
|
||
+ { 0x011C, 0x47, 0x68 }, /* Ĝ to Gh */
|
||
+ { 0x011D, 0x67, 0x68 }, /* ĝ to gh */
|
||
+ { 0x011E, 0x47, 0x00 }, /* Ğ to G */
|
||
+ { 0x011F, 0x67, 0x00 }, /* ğ to g */
|
||
+ { 0x0120, 0x47, 0x00 }, /* Ġ to G */
|
||
+ { 0x0121, 0x67, 0x00 }, /* ġ to g */
|
||
+ { 0x0122, 0x47, 0x00 }, /* Ģ to G */
|
||
+ { 0x0123, 0x67, 0x00 }, /* ģ to g */
|
||
+ { 0x0124, 0x48, 0x68 }, /* Ĥ to Hh */
|
||
+ { 0x0125, 0x68, 0x68 }, /* ĥ to hh */
|
||
+ { 0x0126, 0x48, 0x00 }, /* Ħ to H */
|
||
+ { 0x0127, 0x68, 0x00 }, /* ħ to h */
|
||
+ { 0x0128, 0x49, 0x00 }, /* Ĩ to I */
|
||
+ { 0x0129, 0x69, 0x00 }, /* ĩ to i */
|
||
+ { 0x012A, 0x49, 0x00 }, /* Ī to I */
|
||
+ { 0x012B, 0x69, 0x00 }, /* ī to i */
|
||
+ { 0x012C, 0x49, 0x00 }, /* Ĭ to I */
|
||
+ { 0x012D, 0x69, 0x00 }, /* ĭ to i */
|
||
+ { 0x012E, 0x49, 0x00 }, /* Į to I */
|
||
+ { 0x012F, 0x69, 0x00 }, /* į to i */
|
||
+ { 0x0130, 0x49, 0x00 }, /* İ to I */
|
||
+ { 0x0131, 0x69, 0x00 }, /* ı to i */
|
||
+ { 0x0132, 0x49, 0x4A }, /* IJ to IJ */
|
||
+ { 0x0133, 0x69, 0x6A }, /* ij to ij */
|
||
+ { 0x0134, 0x4A, 0x68 }, /* Ĵ to Jh */
|
||
+ { 0x0135, 0x6A, 0x68 }, /* ĵ to jh */
|
||
+ { 0x0136, 0x4B, 0x00 }, /* Ķ to K */
|
||
+ { 0x0137, 0x6B, 0x00 }, /* ķ to k */
|
||
+ { 0x0138, 0x6B, 0x00 }, /* ĸ to k */
|
||
+ { 0x0139, 0x4C, 0x00 }, /* Ĺ to L */
|
||
+ { 0x013A, 0x6C, 0x00 }, /* ĺ to l */
|
||
+ { 0x013B, 0x4C, 0x00 }, /* Ļ to L */
|
||
+ { 0x013C, 0x6C, 0x00 }, /* ļ to l */
|
||
+ { 0x013D, 0x4C, 0x00 }, /* Ľ to L */
|
||
+ { 0x013E, 0x6C, 0x00 }, /* ľ to l */
|
||
+ { 0x013F, 0x4C, 0x2E }, /* Ŀ to L. */
|
||
+ { 0x0140, 0x6C, 0x2E }, /* ŀ to l. */
|
||
+ { 0x0141, 0x4C, 0x00 }, /* Ł to L */
|
||
+ { 0x0142, 0x6C, 0x00 }, /* ł to l */
|
||
+ { 0x0143, 0x4E, 0x00 }, /* Ń to N */
|
||
+ { 0x0144, 0x6E, 0x00 }, /* ń to n */
|
||
+ { 0x0145, 0x4E, 0x00 }, /* Ņ to N */
|
||
+ { 0x0146, 0x6E, 0x00 }, /* ņ to n */
|
||
+ { 0x0147, 0x4E, 0x00 }, /* Ň to N */
|
||
+ { 0x0148, 0x6E, 0x00 }, /* ň to n */
|
||
+ { 0x0149, 0x27, 0x6E }, /* ʼn to 'n */
|
||
+ { 0x014A, 0x4E, 0x47 }, /* Ŋ to NG */
|
||
+ { 0x014B, 0x6E, 0x67 }, /* ŋ to ng */
|
||
+ { 0x014C, 0x4F, 0x00 }, /* Ō to O */
|
||
+ { 0x014D, 0x6F, 0x00 }, /* ō to o */
|
||
+ { 0x014E, 0x4F, 0x00 }, /* Ŏ to O */
|
||
+ { 0x014F, 0x6F, 0x00 }, /* ŏ to o */
|
||
+ { 0x0150, 0x4F, 0x00 }, /* Ő to O */
|
||
+ { 0x0151, 0x6F, 0x00 }, /* ő to o */
|
||
+ { 0x0152, 0x4F, 0x45 }, /* Œ to OE */
|
||
+ { 0x0153, 0x6F, 0x65 }, /* œ to oe */
|
||
+ { 0x0154, 0x52, 0x00 }, /* Ŕ to R */
|
||
+ { 0x0155, 0x72, 0x00 }, /* ŕ to r */
|
||
+ { 0x0156, 0x52, 0x00 }, /* Ŗ to R */
|
||
+ { 0x0157, 0x72, 0x00 }, /* ŗ to r */
|
||
+ { 0x0158, 0x52, 0x00 }, /* Ř to R */
|
||
+ { 0x0159, 0x72, 0x00 }, /* ř to r */
|
||
+ { 0x015A, 0x53, 0x00 }, /* Ś to S */
|
||
+ { 0x015B, 0x73, 0x00 }, /* ś to s */
|
||
+ { 0x015C, 0x53, 0x68 }, /* Ŝ to Sh */
|
||
+ { 0x015D, 0x73, 0x68 }, /* ŝ to sh */
|
||
+ { 0x015E, 0x53, 0x00 }, /* Ş to S */
|
||
+ { 0x015F, 0x73, 0x00 }, /* ş to s */
|
||
+ { 0x0160, 0x53, 0x00 }, /* Š to S */
|
||
+ { 0x0161, 0x73, 0x00 }, /* š to s */
|
||
+ { 0x0162, 0x54, 0x00 }, /* Ţ to T */
|
||
+ { 0x0163, 0x74, 0x00 }, /* ţ to t */
|
||
+ { 0x0164, 0x54, 0x00 }, /* Ť to T */
|
||
+ { 0x0165, 0x74, 0x00 }, /* ť to t */
|
||
+ { 0x0166, 0x54, 0x00 }, /* Ŧ to T */
|
||
+ { 0x0167, 0x74, 0x00 }, /* ŧ to t */
|
||
+ { 0x0168, 0x55, 0x00 }, /* Ũ to U */
|
||
+ { 0x0169, 0x75, 0x00 }, /* ũ to u */
|
||
+ { 0x016A, 0x55, 0x00 }, /* Ū to U */
|
||
+ { 0x016B, 0x75, 0x00 }, /* ū to u */
|
||
+ { 0x016C, 0x55, 0x00 }, /* Ŭ to U */
|
||
+ { 0x016D, 0x75, 0x00 }, /* ŭ to u */
|
||
+ { 0x016E, 0x55, 0x00 }, /* Ů to U */
|
||
+ { 0x016F, 0x75, 0x00 }, /* ů to u */
|
||
+ { 0x0170, 0x55, 0x00 }, /* Ű to U */
|
||
+ { 0x0171, 0x75, 0x00 }, /* ű to u */
|
||
+ { 0x0172, 0x55, 0x00 }, /* Ų to U */
|
||
+ { 0x0173, 0x75, 0x00 }, /* ų to u */
|
||
+ { 0x0174, 0x57, 0x00 }, /* Ŵ to W */
|
||
+ { 0x0175, 0x77, 0x00 }, /* ŵ to w */
|
||
+ { 0x0176, 0x59, 0x00 }, /* Ŷ to Y */
|
||
+ { 0x0177, 0x79, 0x00 }, /* ŷ to y */
|
||
+ { 0x0178, 0x59, 0x00 }, /* Ÿ to Y */
|
||
+ { 0x0179, 0x5A, 0x00 }, /* Ź to Z */
|
||
+ { 0x017A, 0x7A, 0x00 }, /* ź to z */
|
||
+ { 0x017B, 0x5A, 0x00 }, /* Ż to Z */
|
||
+ { 0x017C, 0x7A, 0x00 }, /* ż to z */
|
||
+ { 0x017D, 0x5A, 0x00 }, /* Ž to Z */
|
||
+ { 0x017E, 0x7A, 0x00 }, /* ž to z */
|
||
+ { 0x017F, 0x73, 0x00 }, /* ſ to s */
|
||
+ { 0x0192, 0x66, 0x00 }, /* ƒ to f */
|
||
+ { 0x0218, 0x53, 0x00 }, /* Ș to S */
|
||
+ { 0x0219, 0x73, 0x00 }, /* ș to s */
|
||
+ { 0x021A, 0x54, 0x00 }, /* Ț to T */
|
||
+ { 0x021B, 0x74, 0x00 }, /* ț to t */
|
||
+ { 0x0386, 0x41, 0x00 }, /* Ά to A */
|
||
+ { 0x0388, 0x45, 0x00 }, /* Έ to E */
|
||
+ { 0x0389, 0x49, 0x00 }, /* Ή to I */
|
||
+ { 0x038A, 0x49, 0x00 }, /* Ί to I */
|
||
+ { 0x038C, 0x4f, 0x00 }, /* Ό to O */
|
||
+ { 0x038E, 0x59, 0x00 }, /* Ύ to Y */
|
||
+ { 0x038F, 0x4f, 0x00 }, /* Ώ to O */
|
||
+ { 0x0390, 0x69, 0x00 }, /* ΐ to i */
|
||
+ { 0x0391, 0x41, 0x00 }, /* Α to A */
|
||
+ { 0x0392, 0x42, 0x00 }, /* Β to B */
|
||
+ { 0x0393, 0x47, 0x00 }, /* Γ to G */
|
||
+ { 0x0394, 0x44, 0x00 }, /* Δ to D */
|
||
+ { 0x0395, 0x45, 0x00 }, /* Ε to E */
|
||
+ { 0x0396, 0x5a, 0x00 }, /* Ζ to Z */
|
||
+ { 0x0397, 0x49, 0x00 }, /* Η to I */
|
||
+ { 0x0398, 0x54, 0x68 }, /* Θ to Th */
|
||
+ { 0x0399, 0x49, 0x00 }, /* Ι to I */
|
||
+ { 0x039A, 0x4b, 0x00 }, /* Κ to K */
|
||
+ { 0x039B, 0x4c, 0x00 }, /* Λ to L */
|
||
+ { 0x039C, 0x4d, 0x00 }, /* Μ to M */
|
||
+ { 0x039D, 0x4e, 0x00 }, /* Ν to N */
|
||
+ { 0x039E, 0x58, 0x00 }, /* Ξ to X */
|
||
+ { 0x039F, 0x4f, 0x00 }, /* Ο to O */
|
||
+ { 0x03A0, 0x50, 0x00 }, /* Π to P */
|
||
+ { 0x03A1, 0x52, 0x00 }, /* Ρ to R */
|
||
+ { 0x03A3, 0x53, 0x00 }, /* Σ to S */
|
||
+ { 0x03A4, 0x54, 0x00 }, /* Τ to T */
|
||
+ { 0x03A5, 0x59, 0x00 }, /* Υ to Y */
|
||
+ { 0x03A6, 0x46, 0x00 }, /* Φ to F */
|
||
+ { 0x03A7, 0x43, 0x68 }, /* Χ to Ch */
|
||
+ { 0x03A8, 0x50, 0x73 }, /* Ψ to Ps */
|
||
+ { 0x03A9, 0x4f, 0x00 }, /* Ω to O */
|
||
+ { 0x03AA, 0x49, 0x00 }, /* Ϊ to I */
|
||
+ { 0x03AB, 0x59, 0x00 }, /* Ϋ to Y */
|
||
+ { 0x03AC, 0x61, 0x00 }, /* ά to a */
|
||
+ { 0x03AD, 0x65, 0x00 }, /* έ to e */
|
||
+ { 0x03AE, 0x69, 0x00 }, /* ή to i */
|
||
+ { 0x03AF, 0x69, 0x00 }, /* ί to i */
|
||
+ { 0x03B1, 0x61, 0x00 }, /* α to a */
|
||
+ { 0x03B2, 0x62, 0x00 }, /* β to b */
|
||
+ { 0x03B3, 0x67, 0x00 }, /* γ to g */
|
||
+ { 0x03B4, 0x64, 0x00 }, /* δ to d */
|
||
+ { 0x03B5, 0x65, 0x00 }, /* ε to e */
|
||
+ { 0x03B6, 0x7a, 0x00 }, /* ζ to z */
|
||
+ { 0x03B7, 0x69, 0x00 }, /* η to i */
|
||
+ { 0x03B8, 0x74, 0x68 }, /* θ to th */
|
||
+ { 0x03B9, 0x69, 0x00 }, /* ι to i */
|
||
+ { 0x03BA, 0x6b, 0x00 }, /* κ to k */
|
||
+ { 0x03BB, 0x6c, 0x00 }, /* λ to l */
|
||
+ { 0x03BC, 0x6d, 0x00 }, /* μ to m */
|
||
+ { 0x03BD, 0x6e, 0x00 }, /* ν to n */
|
||
+ { 0x03BE, 0x78, 0x00 }, /* ξ to x */
|
||
+ { 0x03BF, 0x6f, 0x00 }, /* ο to o */
|
||
+ { 0x03C0, 0x70, 0x00 }, /* π to p */
|
||
+ { 0x03C1, 0x72, 0x00 }, /* ρ to r */
|
||
+ { 0x03C3, 0x73, 0x00 }, /* σ to s */
|
||
+ { 0x03C4, 0x74, 0x00 }, /* τ to t */
|
||
+ { 0x03C5, 0x79, 0x00 }, /* υ to y */
|
||
+ { 0x03C6, 0x66, 0x00 }, /* φ to f */
|
||
+ { 0x03C7, 0x63, 0x68 }, /* χ to ch */
|
||
+ { 0x03C8, 0x70, 0x73 }, /* ψ to ps */
|
||
+ { 0x03C9, 0x6f, 0x00 }, /* ω to o */
|
||
+ { 0x03CA, 0x69, 0x00 }, /* ϊ to i */
|
||
+ { 0x03CB, 0x79, 0x00 }, /* ϋ to y */
|
||
+ { 0x03CC, 0x6f, 0x00 }, /* ό to o */
|
||
+ { 0x03CD, 0x79, 0x00 }, /* ύ to y */
|
||
+ { 0x03CE, 0x69, 0x00 }, /* ώ to i */
|
||
+ { 0x0400, 0x45, 0x00 }, /* Ѐ to E */
|
||
+ { 0x0401, 0x45, 0x00 }, /* Ё to E */
|
||
+ { 0x0402, 0x44, 0x00 }, /* Ђ to D */
|
||
+ { 0x0403, 0x47, 0x00 }, /* Ѓ to G */
|
||
+ { 0x0404, 0x45, 0x00 }, /* Є to E */
|
||
+ { 0x0405, 0x5a, 0x00 }, /* Ѕ to Z */
|
||
+ { 0x0406, 0x49, 0x00 }, /* І to I */
|
||
+ { 0x0407, 0x49, 0x00 }, /* Ї to I */
|
||
+ { 0x0408, 0x4a, 0x00 }, /* Ј to J */
|
||
+ { 0x0409, 0x49, 0x00 }, /* Љ to I */
|
||
+ { 0x040A, 0x4e, 0x00 }, /* Њ to N */
|
||
+ { 0x040B, 0x44, 0x00 }, /* Ћ to D */
|
||
+ { 0x040C, 0x4b, 0x00 }, /* Ќ to K */
|
||
+ { 0x040D, 0x49, 0x00 }, /* Ѝ to I */
|
||
+ { 0x040E, 0x55, 0x00 }, /* Ў to U */
|
||
+ { 0x040F, 0x44, 0x00 }, /* Џ to D */
|
||
+ { 0x0410, 0x41, 0x00 }, /* А to A */
|
||
+ { 0x0411, 0x42, 0x00 }, /* Б to B */
|
||
+ { 0x0412, 0x56, 0x00 }, /* В to V */
|
||
+ { 0x0413, 0x47, 0x00 }, /* Г to G */
|
||
+ { 0x0414, 0x44, 0x00 }, /* Д to D */
|
||
+ { 0x0415, 0x45, 0x00 }, /* Е to E */
|
||
+ { 0x0416, 0x5a, 0x68 }, /* Ж to Zh */
|
||
+ { 0x0417, 0x5a, 0x00 }, /* З to Z */
|
||
+ { 0x0418, 0x49, 0x00 }, /* И to I */
|
||
+ { 0x0419, 0x49, 0x00 }, /* Й to I */
|
||
+ { 0x041A, 0x4b, 0x00 }, /* К to K */
|
||
+ { 0x041B, 0x4c, 0x00 }, /* Л to L */
|
||
+ { 0x041C, 0x4d, 0x00 }, /* М to M */
|
||
+ { 0x041D, 0x4e, 0x00 }, /* Н to N */
|
||
+ { 0x041E, 0x4f, 0x00 }, /* О to O */
|
||
+ { 0x041F, 0x50, 0x00 }, /* П to P */
|
||
+ { 0x0420, 0x52, 0x00 }, /* Р to R */
|
||
+ { 0x0421, 0x53, 0x00 }, /* С to S */
|
||
+ { 0x0422, 0x54, 0x00 }, /* Т to T */
|
||
+ { 0x0423, 0x55, 0x00 }, /* У to U */
|
||
+ { 0x0424, 0x46, 0x00 }, /* Ф to F */
|
||
+ { 0x0425, 0x4b, 0x68 }, /* Х to Kh */
|
||
+ { 0x0426, 0x54, 0x63 }, /* Ц to Tc */
|
||
+ { 0x0427, 0x43, 0x68 }, /* Ч to Ch */
|
||
+ { 0x0428, 0x53, 0x68 }, /* Ш to Sh */
|
||
+ { 0x0429, 0x53, 0x68 }, /* Щ to Shch */
|
||
+ { 0x042A, 0x61, 0x00 }, /* to A */
|
||
+ { 0x042B, 0x59, 0x00 }, /* Ы to Y */
|
||
+ { 0x042C, 0x59, 0x00 }, /* to Y */
|
||
+ { 0x042D, 0x45, 0x00 }, /* Э to E */
|
||
+ { 0x042E, 0x49, 0x75 }, /* Ю to Iu */
|
||
+ { 0x042F, 0x49, 0x61 }, /* Я to Ia */
|
||
+ { 0x0430, 0x61, 0x00 }, /* а to a */
|
||
+ { 0x0431, 0x62, 0x00 }, /* б to b */
|
||
+ { 0x0432, 0x76, 0x00 }, /* в to v */
|
||
+ { 0x0433, 0x67, 0x00 }, /* г to g */
|
||
+ { 0x0434, 0x64, 0x00 }, /* д to d */
|
||
+ { 0x0435, 0x65, 0x00 }, /* е to e */
|
||
+ { 0x0436, 0x7a, 0x68 }, /* ж to zh */
|
||
+ { 0x0437, 0x7a, 0x00 }, /* з to z */
|
||
+ { 0x0438, 0x69, 0x00 }, /* и to i */
|
||
+ { 0x0439, 0x69, 0x00 }, /* й to i */
|
||
+ { 0x043A, 0x6b, 0x00 }, /* к to k */
|
||
+ { 0x043B, 0x6c, 0x00 }, /* л to l */
|
||
+ { 0x043C, 0x6d, 0x00 }, /* м to m */
|
||
+ { 0x043D, 0x6e, 0x00 }, /* н to n */
|
||
+ { 0x043E, 0x6f, 0x00 }, /* о to o */
|
||
+ { 0x043F, 0x70, 0x00 }, /* п to p */
|
||
+ { 0x0440, 0x72, 0x00 }, /* р to r */
|
||
+ { 0x0441, 0x73, 0x00 }, /* с to s */
|
||
+ { 0x0442, 0x74, 0x00 }, /* т to t */
|
||
+ { 0x0443, 0x75, 0x00 }, /* у to u */
|
||
+ { 0x0444, 0x66, 0x00 }, /* ф to f */
|
||
+ { 0x0445, 0x6b, 0x68 }, /* х to kh */
|
||
+ { 0x0446, 0x74, 0x63 }, /* ц to tc */
|
||
+ { 0x0447, 0x63, 0x68 }, /* ч to ch */
|
||
+ { 0x0448, 0x73, 0x68 }, /* ш to sh */
|
||
+ { 0x0449, 0x73, 0x68 }, /* щ to shch */
|
||
+ { 0x044A, 0x61, 0x00 }, /* to a */
|
||
+ { 0x044B, 0x79, 0x00 }, /* ы to y */
|
||
+ { 0x044C, 0x79, 0x00 }, /* to y */
|
||
+ { 0x044D, 0x65, 0x00 }, /* э to e */
|
||
+ { 0x044E, 0x69, 0x75 }, /* ю to iu */
|
||
+ { 0x044F, 0x69, 0x61 }, /* я to ia */
|
||
+ { 0x0450, 0x65, 0x00 }, /* ѐ to e */
|
||
+ { 0x0451, 0x65, 0x00 }, /* ё to e */
|
||
+ { 0x0452, 0x64, 0x00 }, /* ђ to d */
|
||
+ { 0x0453, 0x67, 0x00 }, /* ѓ to g */
|
||
+ { 0x0454, 0x65, 0x00 }, /* є to e */
|
||
+ { 0x0455, 0x7a, 0x00 }, /* ѕ to z */
|
||
+ { 0x0456, 0x69, 0x00 }, /* і to i */
|
||
+ { 0x0457, 0x69, 0x00 }, /* ї to i */
|
||
+ { 0x0458, 0x6a, 0x00 }, /* ј to j */
|
||
+ { 0x0459, 0x69, 0x00 }, /* љ to i */
|
||
+ { 0x045A, 0x6e, 0x00 }, /* њ to n */
|
||
+ { 0x045B, 0x64, 0x00 }, /* ћ to d */
|
||
+ { 0x045C, 0x6b, 0x00 }, /* ќ to k */
|
||
+ { 0x045D, 0x69, 0x00 }, /* ѝ to i */
|
||
+ { 0x045E, 0x75, 0x00 }, /* ў to u */
|
||
+ { 0x045F, 0x64, 0x00 }, /* џ to d */
|
||
+ { 0x1E02, 0x42, 0x00 }, /* Ḃ to B */
|
||
+ { 0x1E03, 0x62, 0x00 }, /* ḃ to b */
|
||
+ { 0x1E0A, 0x44, 0x00 }, /* Ḋ to D */
|
||
+ { 0x1E0B, 0x64, 0x00 }, /* ḋ to d */
|
||
+ { 0x1E1E, 0x46, 0x00 }, /* Ḟ to F */
|
||
+ { 0x1E1F, 0x66, 0x00 }, /* ḟ to f */
|
||
+ { 0x1E40, 0x4D, 0x00 }, /* Ṁ to M */
|
||
+ { 0x1E41, 0x6D, 0x00 }, /* ṁ to m */
|
||
+ { 0x1E56, 0x50, 0x00 }, /* Ṗ to P */
|
||
+ { 0x1E57, 0x70, 0x00 }, /* ṗ to p */
|
||
+ { 0x1E60, 0x53, 0x00 }, /* Ṡ to S */
|
||
+ { 0x1E61, 0x73, 0x00 }, /* ṡ to s */
|
||
+ { 0x1E6A, 0x54, 0x00 }, /* Ṫ to T */
|
||
+ { 0x1E6B, 0x74, 0x00 }, /* ṫ to t */
|
||
+ { 0x1E80, 0x57, 0x00 }, /* Ẁ to W */
|
||
+ { 0x1E81, 0x77, 0x00 }, /* ẁ to w */
|
||
+ { 0x1E82, 0x57, 0x00 }, /* Ẃ to W */
|
||
+ { 0x1E83, 0x77, 0x00 }, /* ẃ to w */
|
||
+ { 0x1E84, 0x57, 0x00 }, /* Ẅ to W */
|
||
+ { 0x1E85, 0x77, 0x00 }, /* ẅ to w */
|
||
+ { 0x1EF2, 0x59, 0x00 }, /* Ỳ to Y */
|
||
+ { 0x1EF3, 0x79, 0x00 }, /* ỳ to y */
|
||
+ { 0xFB00, 0x66, 0x66 }, /* ff to ff */
|
||
+ { 0xFB01, 0x66, 0x69 }, /* fi to fi */
|
||
+ { 0xFB02, 0x66, 0x6C }, /* fl to fl */
|
||
+ { 0xFB05, 0x73, 0x74 }, /* ſt to st */
|
||
+ { 0xFB06, 0x73, 0x74 }, /* st to st */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Convert the input string from UTF-8 into pure ASCII by converting
|
||
+** all non-ASCII characters to some combination of characters in the
|
||
+** ASCII subset.
|
||
+**
|
||
+** The returned string might contain more characters than the input.
|
||
+**
|
||
+** Space to hold the returned string comes from sqlite3_malloc() and
|
||
+** should be freed by the caller.
|
||
+*/
|
||
+static unsigned char *transliterate(const unsigned char *zIn, int nIn){
|
||
+ unsigned char *zOut = sqlite3_malloc( nIn*4 + 1 );
|
||
+ int c, sz, nOut;
|
||
+ if( zOut==0 ) return 0;
|
||
+ nOut = 0;
|
||
+ while( nIn>0 ){
|
||
+ c = utf8Read(zIn, nIn, &sz);
|
||
+ zIn += sz;
|
||
+ nIn -= sz;
|
||
+ if( c<=127 ){
|
||
+ zOut[nOut++] = c;
|
||
+ }else{
|
||
+ int xTop, xBtm, x;
|
||
+ xTop = sizeof(translit)/sizeof(translit[0]) - 1;
|
||
+ xBtm = 0;
|
||
+ while( xTop>=xBtm ){
|
||
+ x = (xTop + xBtm)/2;
|
||
+ if( translit[x].cFrom==c ){
|
||
+ zOut[nOut++] = translit[x].cTo0;
|
||
+ if( translit[x].cTo1 ){
|
||
+ zOut[nOut++] = translit[x].cTo1;
|
||
+ /* Add an extra "ch" after the "sh" for Щ and щ */
|
||
+ if( c==0x0429 || c== 0x0449 ){
|
||
+ zOut[nOut++] = 'c';
|
||
+ zOut[nOut++] = 'h';
|
||
+ }
|
||
+ }
|
||
+ c = 0;
|
||
+ break;
|
||
+ }else if( translit[x].cFrom>c ){
|
||
+ xTop = x-1;
|
||
+ }else{
|
||
+ xBtm = x+1;
|
||
+ }
|
||
+ }
|
||
+ if( c ) zOut[nOut++] = '?';
|
||
+ }
|
||
+ }
|
||
+ zOut[nOut] = 0;
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the number of characters in the shortest prefix of the input
|
||
+** string that transliterates to an ASCII string nTrans bytes or longer.
|
||
+** Or, if the transliteration of the input string is less than nTrans
|
||
+** bytes in size, return the number of characters in the input string.
|
||
+*/
|
||
+static int translen_to_charlen(const char *zIn, int nIn, int nTrans){
|
||
+ int i, c, sz, nOut;
|
||
+ int nChar;
|
||
+
|
||
+ i = nOut = 0;
|
||
+ for(nChar=0; i<nIn && nOut<nTrans; nChar++){
|
||
+ c = utf8Read((const unsigned char *)&zIn[i], nIn-i, &sz);
|
||
+ i += sz;
|
||
+
|
||
+ nOut++;
|
||
+ if( c>=128 ){
|
||
+ int xTop, xBtm, x;
|
||
+ xTop = sizeof(translit)/sizeof(translit[0]) - 1;
|
||
+ xBtm = 0;
|
||
+ while( xTop>=xBtm ){
|
||
+ x = (xTop + xBtm)/2;
|
||
+ if( translit[x].cFrom==c ){
|
||
+ if( translit[x].cTo1 ) nOut++;
|
||
+ if( c==0x0429 || c== 0x0449 ) nOut += 2;
|
||
+ break;
|
||
+ }else if( translit[x].cFrom>c ){
|
||
+ xTop = x-1;
|
||
+ }else{
|
||
+ xBtm = x+1;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return nChar;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** spellfix1_translit(X)
|
||
+**
|
||
+** Convert a string that contains non-ASCII Roman characters into
|
||
+** pure ASCII.
|
||
+*/
|
||
+static void transliterateSqlFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zIn = sqlite3_value_text(argv[0]);
|
||
+ int nIn = sqlite3_value_bytes(argv[0]);
|
||
+ unsigned char *zOut = transliterate(zIn, nIn);
|
||
+ if( zOut==0 ){
|
||
+ sqlite3_result_error_nomem(context);
|
||
+ }else{
|
||
+ sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** spellfix1_scriptcode(X)
|
||
+**
|
||
+** Try to determine the dominant script used by the word X and return
|
||
+** its ISO 15924 numeric code.
|
||
+**
|
||
+** The current implementation only understands the following scripts:
|
||
+**
|
||
+** 215 (Latin)
|
||
+** 220 (Cyrillic)
|
||
+** 200 (Greek)
|
||
+**
|
||
+** This routine will return 998 if the input X contains characters from
|
||
+** two or more of the above scripts or 999 if X contains no characters
|
||
+** from any of the above scripts.
|
||
+*/
|
||
+static void scriptCodeSqlFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zIn = sqlite3_value_text(argv[0]);
|
||
+ int nIn = sqlite3_value_bytes(argv[0]);
|
||
+ int c, sz;
|
||
+ int scriptMask = 0;
|
||
+ int res;
|
||
+# define SCRIPT_LATIN 0x0001
|
||
+# define SCRIPT_CYRILLIC 0x0002
|
||
+# define SCRIPT_GREEK 0x0004
|
||
+
|
||
+ while( nIn>0 ){
|
||
+ c = utf8Read(zIn, nIn, &sz);
|
||
+ zIn += sz;
|
||
+ nIn -= sz;
|
||
+ if( c<0x02af ){
|
||
+ scriptMask |= SCRIPT_LATIN;
|
||
+ }else if( c>=0x0400 && c<=0x04ff ){
|
||
+ scriptMask |= SCRIPT_CYRILLIC;
|
||
+ }else if( c>=0x0386 && c<=0x03ce ){
|
||
+ scriptMask |= SCRIPT_GREEK;
|
||
+ }
|
||
+ }
|
||
+ switch( scriptMask ){
|
||
+ case 0: res = 999; break;
|
||
+ case SCRIPT_LATIN: res = 215; break;
|
||
+ case SCRIPT_CYRILLIC: res = 220; break;
|
||
+ case SCRIPT_GREEK: res = 200; break;
|
||
+ default: res = 998; break;
|
||
+ }
|
||
+ sqlite3_result_int(context, res);
|
||
+}
|
||
+
|
||
+/* End transliterate
|
||
+******************************************************************************
|
||
+******************************************************************************
|
||
+** Begin spellfix1 virtual table.
|
||
+*/
|
||
+
|
||
+/* Maximum length of a phonehash used for querying the shadow table */
|
||
+#define SPELLFIX_MX_HASH 8
|
||
+
|
||
+/* Maximum number of hash strings to examine per query */
|
||
+#define SPELLFIX_MX_RUN 1
|
||
+
|
||
+typedef struct spellfix1_vtab spellfix1_vtab;
|
||
+typedef struct spellfix1_cursor spellfix1_cursor;
|
||
+
|
||
+/* Fuzzy-search virtual table object */
|
||
+struct spellfix1_vtab {
|
||
+ sqlite3_vtab base; /* Base class - must be first */
|
||
+ sqlite3 *db; /* Database connection */
|
||
+ char *zDbName; /* Name of database holding this table */
|
||
+ char *zTableName; /* Name of the virtual table */
|
||
+ char *zCostTable; /* Table holding edit-distance cost numbers */
|
||
+ EditDist3Config *pConfig3; /* Parsed edit distance costs */
|
||
+};
|
||
+
|
||
+/* Fuzzy-search cursor object */
|
||
+struct spellfix1_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ spellfix1_vtab *pVTab; /* The table to which this cursor belongs */
|
||
+ char *zPattern; /* rhs of MATCH clause */
|
||
+ int nRow; /* Number of rows of content */
|
||
+ int nAlloc; /* Number of allocated rows */
|
||
+ int iRow; /* Current row of content */
|
||
+ int iLang; /* Value of the langid= constraint */
|
||
+ int iTop; /* Value of the top= constraint */
|
||
+ int iScope; /* Value of the scope= constraint */
|
||
+ int nSearch; /* Number of vocabulary items checked */
|
||
+ sqlite3_stmt *pFullScan; /* Shadow query for a full table scan */
|
||
+ struct spellfix1_row { /* For each row of content */
|
||
+ sqlite3_int64 iRowid; /* Rowid for this row */
|
||
+ char *zWord; /* Text for this row */
|
||
+ int iRank; /* Rank for this row */
|
||
+ int iDistance; /* Distance from pattern for this row */
|
||
+ int iScore; /* Score for sorting */
|
||
+ int iMatchlen; /* Value of matchlen column (or -1) */
|
||
+ char zHash[SPELLFIX_MX_HASH]; /* the phonehash used for this match */
|
||
+ } *a;
|
||
+};
|
||
+
|
||
+/*
|
||
+** Construct one or more SQL statements from the format string given
|
||
+** and then evaluate those statements. The success code is written
|
||
+** into *pRc.
|
||
+**
|
||
+** If *pRc is initially non-zero then this routine is a no-op.
|
||
+*/
|
||
+static void spellfix1DbExec(
|
||
+ int *pRc, /* Success code */
|
||
+ sqlite3 *db, /* Database in which to run SQL */
|
||
+ const char *zFormat, /* Format string for SQL */
|
||
+ ... /* Arguments to the format string */
|
||
+){
|
||
+ va_list ap;
|
||
+ char *zSql;
|
||
+ if( *pRc ) return;
|
||
+ va_start(ap, zFormat);
|
||
+ zSql = sqlite3_vmprintf(zFormat, ap);
|
||
+ va_end(ap);
|
||
+ if( zSql==0 ){
|
||
+ *pRc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ *pRc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** xDisconnect/xDestroy method for the fuzzy-search module.
|
||
+*/
|
||
+static int spellfix1Uninit(int isDestroy, sqlite3_vtab *pVTab){
|
||
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
||
+ int rc = SQLITE_OK;
|
||
+ if( isDestroy ){
|
||
+ sqlite3 *db = p->db;
|
||
+ spellfix1DbExec(&rc, db, "DROP TABLE IF EXISTS \"%w\".\"%w_vocab\"",
|
||
+ p->zDbName, p->zTableName);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ sqlite3_free(p->zTableName);
|
||
+ editDist3ConfigDelete(p->pConfig3);
|
||
+ sqlite3_free(p->zCostTable);
|
||
+ sqlite3_free(p);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+static int spellfix1Disconnect(sqlite3_vtab *pVTab){
|
||
+ return spellfix1Uninit(0, pVTab);
|
||
+}
|
||
+static int spellfix1Destroy(sqlite3_vtab *pVTab){
|
||
+ return spellfix1Uninit(1, pVTab);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Make a copy of a string. Remove leading and trailing whitespace
|
||
+** and dequote it.
|
||
+*/
|
||
+static char *spellfix1Dequote(const char *zIn){
|
||
+ char *zOut;
|
||
+ int i, j;
|
||
+ char c;
|
||
+ while( isspace(zIn[0]) ) zIn++;
|
||
+ zOut = sqlite3_mprintf("%s", zIn);
|
||
+ if( zOut==0 ) return 0;
|
||
+ i = (int)strlen(zOut);
|
||
+#if 0 /* The parser will never leave spaces at the end */
|
||
+ while( i>0 && isspace(zOut[i-1]) ){ i--; }
|
||
+#endif
|
||
+ zOut[i] = 0;
|
||
+ c = zOut[0];
|
||
+ if( c=='\'' || c=='"' ){
|
||
+ for(i=1, j=0; ALWAYS(zOut[i]); i++){
|
||
+ zOut[j++] = zOut[i];
|
||
+ if( zOut[i]==c ){
|
||
+ if( zOut[i+1]==c ){
|
||
+ i++;
|
||
+ }else{
|
||
+ zOut[j-1] = 0;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ return zOut;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** xConnect/xCreate method for the spellfix1 module. Arguments are:
|
||
+**
|
||
+** argv[0] -> module name ("spellfix1")
|
||
+** argv[1] -> database name
|
||
+** argv[2] -> table name
|
||
+** argv[3].. -> optional arguments (i.e. "edit_cost_table" parameter)
|
||
+*/
|
||
+static int spellfix1Init(
|
||
+ int isCreate,
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVTab,
|
||
+ char **pzErr
|
||
+){
|
||
+ spellfix1_vtab *pNew = 0;
|
||
+ /* const char *zModule = argv[0]; // not used */
|
||
+ const char *zDbName = argv[1];
|
||
+ const char *zTableName = argv[2];
|
||
+ int nDbName;
|
||
+ int rc = SQLITE_OK;
|
||
+ int i;
|
||
+
|
||
+ nDbName = (int)strlen(zDbName);
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) + nDbName + 1);
|
||
+ if( pNew==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ pNew->zDbName = (char*)&pNew[1];
|
||
+ memcpy(pNew->zDbName, zDbName, nDbName+1);
|
||
+ pNew->zTableName = sqlite3_mprintf("%s", zTableName);
|
||
+ pNew->db = db;
|
||
+ if( pNew->zTableName==0 ){
|
||
+ rc = SQLITE_NOMEM;
|
||
+ }else{
|
||
+ rc = sqlite3_declare_vtab(db,
|
||
+ "CREATE TABLE x(word,rank,distance,langid, "
|
||
+ "score, matchlen, phonehash HIDDEN, "
|
||
+ "top HIDDEN, scope HIDDEN, srchcnt HIDDEN, "
|
||
+ "soundslike HIDDEN, command HIDDEN)"
|
||
+ );
|
||
+#define SPELLFIX_COL_WORD 0
|
||
+#define SPELLFIX_COL_RANK 1
|
||
+#define SPELLFIX_COL_DISTANCE 2
|
||
+#define SPELLFIX_COL_LANGID 3
|
||
+#define SPELLFIX_COL_SCORE 4
|
||
+#define SPELLFIX_COL_MATCHLEN 5
|
||
+#define SPELLFIX_COL_PHONEHASH 6
|
||
+#define SPELLFIX_COL_TOP 7
|
||
+#define SPELLFIX_COL_SCOPE 8
|
||
+#define SPELLFIX_COL_SRCHCNT 9
|
||
+#define SPELLFIX_COL_SOUNDSLIKE 10
|
||
+#define SPELLFIX_COL_COMMAND 11
|
||
+ }
|
||
+ if( rc==SQLITE_OK && isCreate ){
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n"
|
||
+ " id INTEGER PRIMARY KEY,\n"
|
||
+ " rank INT,\n"
|
||
+ " langid INT,\n"
|
||
+ " word TEXT,\n"
|
||
+ " k1 TEXT,\n"
|
||
+ " k2 TEXT\n"
|
||
+ ");\n",
|
||
+ zDbName, zTableName
|
||
+ );
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
|
||
+ "ON \"%w_vocab\"(langid,k2);",
|
||
+ zDbName, zTableName, zTableName
|
||
+ );
|
||
+ }
|
||
+ for(i=3; rc==SQLITE_OK && i<argc; i++){
|
||
+ if( strncmp(argv[i],"edit_cost_table=",16)==0 && pNew->zCostTable==0 ){
|
||
+ pNew->zCostTable = spellfix1Dequote(&argv[i][16]);
|
||
+ if( pNew->zCostTable==0 ) rc = SQLITE_NOMEM;
|
||
+ continue;
|
||
+ }
|
||
+ *pzErr = sqlite3_mprintf("bad argument to spellfix1(): \"%s\"", argv[i]);
|
||
+ rc = SQLITE_ERROR;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if( rc && pNew ){
|
||
+ *ppVTab = 0;
|
||
+ spellfix1Uninit(0, &pNew->base);
|
||
+ }else{
|
||
+ *ppVTab = (sqlite3_vtab *)pNew;
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The xConnect and xCreate methods
|
||
+*/
|
||
+static int spellfix1Connect(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVTab,
|
||
+ char **pzErr
|
||
+){
|
||
+ return spellfix1Init(0, db, pAux, argc, argv, ppVTab, pzErr);
|
||
+}
|
||
+static int spellfix1Create(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVTab,
|
||
+ char **pzErr
|
||
+){
|
||
+ return spellfix1Init(1, db, pAux, argc, argv, ppVTab, pzErr);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Clear all of the content from a cursor.
|
||
+*/
|
||
+static void spellfix1ResetCursor(spellfix1_cursor *pCur){
|
||
+ int i;
|
||
+ for(i=0; i<pCur->nRow; i++){
|
||
+ sqlite3_free(pCur->a[i].zWord);
|
||
+ }
|
||
+ pCur->nRow = 0;
|
||
+ pCur->iRow = 0;
|
||
+ pCur->nSearch = 0;
|
||
+ if( pCur->pFullScan ){
|
||
+ sqlite3_finalize(pCur->pFullScan);
|
||
+ pCur->pFullScan = 0;
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Resize the cursor to hold up to N rows of content
|
||
+*/
|
||
+static void spellfix1ResizeCursor(spellfix1_cursor *pCur, int N){
|
||
+ struct spellfix1_row *aNew;
|
||
+ assert( N>=pCur->nRow );
|
||
+ aNew = sqlite3_realloc(pCur->a, sizeof(pCur->a[0])*N);
|
||
+ if( aNew==0 && N>0 ){
|
||
+ spellfix1ResetCursor(pCur);
|
||
+ sqlite3_free(pCur->a);
|
||
+ pCur->nAlloc = 0;
|
||
+ pCur->a = 0;
|
||
+ }else{
|
||
+ pCur->nAlloc = N;
|
||
+ pCur->a = aNew;
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Close a fuzzy-search cursor.
|
||
+*/
|
||
+static int spellfix1Close(sqlite3_vtab_cursor *cur){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
||
+ spellfix1ResetCursor(pCur);
|
||
+ spellfix1ResizeCursor(pCur, 0);
|
||
+ sqlite3_free(pCur->zPattern);
|
||
+ sqlite3_free(pCur);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Search for terms of these forms:
|
||
+**
|
||
+** (A) word MATCH $str
|
||
+** (B) langid == $langid
|
||
+** (C) top = $top
|
||
+** (D) scope = $scope
|
||
+** (E) distance < $distance
|
||
+** (F) distance <= $distance
|
||
+** (G) rowid = $rowid
|
||
+**
|
||
+** The plan number is a bit mask formed with these bits:
|
||
+**
|
||
+** 0x01 (A) is found
|
||
+** 0x02 (B) is found
|
||
+** 0x04 (C) is found
|
||
+** 0x08 (D) is found
|
||
+** 0x10 (E) is found
|
||
+** 0x20 (F) is found
|
||
+** 0x40 (G) is found
|
||
+**
|
||
+** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid
|
||
+** if specified and in that order.
|
||
+*/
|
||
+static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||
+ int iPlan = 0;
|
||
+ int iLangTerm = -1;
|
||
+ int iTopTerm = -1;
|
||
+ int iScopeTerm = -1;
|
||
+ int iDistTerm = -1;
|
||
+ int iRowidTerm = -1;
|
||
+ int i;
|
||
+ const struct sqlite3_index_constraint *pConstraint;
|
||
+ pConstraint = pIdxInfo->aConstraint;
|
||
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||
+ if( pConstraint->usable==0 ) continue;
|
||
+
|
||
+ /* Terms of the form: word MATCH $str */
|
||
+ if( (iPlan & 1)==0
|
||
+ && pConstraint->iColumn==SPELLFIX_COL_WORD
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH
|
||
+ ){
|
||
+ iPlan |= 1;
|
||
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||
+ pIdxInfo->aConstraintUsage[i].omit = 1;
|
||
+ }
|
||
+
|
||
+ /* Terms of the form: langid = $langid */
|
||
+ if( (iPlan & 2)==0
|
||
+ && pConstraint->iColumn==SPELLFIX_COL_LANGID
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 2;
|
||
+ iLangTerm = i;
|
||
+ }
|
||
+
|
||
+ /* Terms of the form: top = $top */
|
||
+ if( (iPlan & 4)==0
|
||
+ && pConstraint->iColumn==SPELLFIX_COL_TOP
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 4;
|
||
+ iTopTerm = i;
|
||
+ }
|
||
+
|
||
+ /* Terms of the form: scope = $scope */
|
||
+ if( (iPlan & 8)==0
|
||
+ && pConstraint->iColumn==SPELLFIX_COL_SCOPE
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 8;
|
||
+ iScopeTerm = i;
|
||
+ }
|
||
+
|
||
+ /* Terms of the form: distance < $dist or distance <= $dist */
|
||
+ if( (iPlan & (16|32))==0
|
||
+ && pConstraint->iColumn==SPELLFIX_COL_DISTANCE
|
||
+ && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||
+ || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE)
|
||
+ ){
|
||
+ iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32;
|
||
+ iDistTerm = i;
|
||
+ }
|
||
+
|
||
+ /* Terms of the form: distance < $dist or distance <= $dist */
|
||
+ if( (iPlan & 64)==0
|
||
+ && pConstraint->iColumn<0
|
||
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||
+ ){
|
||
+ iPlan |= 64;
|
||
+ iRowidTerm = i;
|
||
+ }
|
||
+ }
|
||
+ if( iPlan&1 ){
|
||
+ int idx = 2;
|
||
+ pIdxInfo->idxNum = iPlan;
|
||
+ if( pIdxInfo->nOrderBy==1
|
||
+ && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE
|
||
+ && pIdxInfo->aOrderBy[0].desc==0
|
||
+ ){
|
||
+ pIdxInfo->orderByConsumed = 1; /* Default order by iScore */
|
||
+ }
|
||
+ if( iPlan&2 ){
|
||
+ pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++;
|
||
+ pIdxInfo->aConstraintUsage[iLangTerm].omit = 1;
|
||
+ }
|
||
+ if( iPlan&4 ){
|
||
+ pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++;
|
||
+ pIdxInfo->aConstraintUsage[iTopTerm].omit = 1;
|
||
+ }
|
||
+ if( iPlan&8 ){
|
||
+ pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++;
|
||
+ pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1;
|
||
+ }
|
||
+ if( iPlan&(16|32) ){
|
||
+ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++;
|
||
+ pIdxInfo->aConstraintUsage[iDistTerm].omit = 1;
|
||
+ }
|
||
+ pIdxInfo->estimatedCost = 1e5;
|
||
+ }else if( (iPlan & 64) ){
|
||
+ pIdxInfo->idxNum = 64;
|
||
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
|
||
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
|
||
+ pIdxInfo->estimatedCost = 5;
|
||
+ }else{
|
||
+ pIdxInfo->idxNum = 0;
|
||
+ pIdxInfo->estimatedCost = 1e50;
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open a new fuzzy-search cursor.
|
||
+*/
|
||
+static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
||
+ spellfix1_cursor *pCur;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ pCur->pVTab = p;
|
||
+ *ppCursor = &pCur->base;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Adjust a distance measurement by the words rank in order to show
|
||
+** preference to common words.
|
||
+*/
|
||
+static int spellfix1Score(int iDistance, int iRank){
|
||
+ int iLog2;
|
||
+ for(iLog2=0; iRank>0; iLog2++, iRank>>=1){}
|
||
+ return iDistance + 32 - iLog2;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compare two spellfix1_row objects for sorting purposes in qsort() such
|
||
+** that they sort in order of increasing distance.
|
||
+*/
|
||
+static int spellfix1RowCompare(const void *A, const void *B){
|
||
+ const struct spellfix1_row *a = (const struct spellfix1_row*)A;
|
||
+ const struct spellfix1_row *b = (const struct spellfix1_row*)B;
|
||
+ return a->iScore - b->iScore;
|
||
+}
|
||
+
|
||
+/*
|
||
+** A structure used to pass information from spellfix1FilterForMatch()
|
||
+** into spellfix1RunQuery().
|
||
+*/
|
||
+typedef struct MatchQuery {
|
||
+ spellfix1_cursor *pCur; /* The cursor being queried */
|
||
+ sqlite3_stmt *pStmt; /* shadow table query statment */
|
||
+ char zHash[SPELLFIX_MX_HASH]; /* The current phonehash for zPattern */
|
||
+ const char *zPattern; /* Transliterated input string */
|
||
+ int nPattern; /* Length of zPattern */
|
||
+ EditDist3FromString *pMatchStr3; /* Original unicode string */
|
||
+ EditDist3Config *pConfig3; /* Edit-distance cost coefficients */
|
||
+ const EditDist3Lang *pLang; /* The selected language coefficients */
|
||
+ int iLang; /* The language id */
|
||
+ int iScope; /* Default scope */
|
||
+ int iMaxDist; /* Maximum allowed edit distance, or -1 */
|
||
+ int rc; /* Error code */
|
||
+ int nRun; /* Number of prior runs for the same zPattern */
|
||
+ char azPrior[SPELLFIX_MX_RUN][SPELLFIX_MX_HASH]; /* Prior hashes */
|
||
+} MatchQuery;
|
||
+
|
||
+/*
|
||
+** Run a query looking for the best matches against zPattern using
|
||
+** zHash as the character class seed hash.
|
||
+*/
|
||
+static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){
|
||
+ const char *zK1;
|
||
+ const char *zWord;
|
||
+ int iDist;
|
||
+ int iRank;
|
||
+ int iScore;
|
||
+ int iWorst = 0;
|
||
+ int idx;
|
||
+ int idxWorst = -1;
|
||
+ int i;
|
||
+ int iScope = p->iScope;
|
||
+ spellfix1_cursor *pCur = p->pCur;
|
||
+ sqlite3_stmt *pStmt = p->pStmt;
|
||
+ char zHash1[SPELLFIX_MX_HASH];
|
||
+ char zHash2[SPELLFIX_MX_HASH];
|
||
+ char *zClass;
|
||
+ int nClass;
|
||
+ int rc;
|
||
+
|
||
+ if( pCur->a==0 || p->rc ) return; /* Prior memory allocation failure */
|
||
+ zClass = (char*)phoneticHash((unsigned char*)zQuery, nQuery);
|
||
+ if( zClass==0 ){
|
||
+ p->rc = SQLITE_NOMEM;
|
||
+ return;
|
||
+ }
|
||
+ nClass = (int)strlen(zClass);
|
||
+ if( nClass>SPELLFIX_MX_HASH-2 ){
|
||
+ nClass = SPELLFIX_MX_HASH-2;
|
||
+ zClass[nClass] = 0;
|
||
+ }
|
||
+ if( nClass<=iScope ){
|
||
+ if( nClass>2 ){
|
||
+ iScope = nClass-1;
|
||
+ }else{
|
||
+ iScope = nClass;
|
||
+ }
|
||
+ }
|
||
+ memcpy(zHash1, zClass, iScope);
|
||
+ sqlite3_free(zClass);
|
||
+ zHash1[iScope] = 0;
|
||
+ memcpy(zHash2, zHash1, iScope);
|
||
+ zHash2[iScope] = 'Z';
|
||
+ zHash2[iScope+1] = 0;
|
||
+#if SPELLFIX_MX_RUN>1
|
||
+ for(i=0; i<p->nRun; i++){
|
||
+ if( strcmp(p->azPrior[i], zHash1)==0 ) return;
|
||
+ }
|
||
+#endif
|
||
+ assert( p->nRun<SPELLFIX_MX_RUN );
|
||
+ memcpy(p->azPrior[p->nRun++], zHash1, iScope+1);
|
||
+ if( sqlite3_bind_text(pStmt, 1, zHash1, -1, SQLITE_STATIC)==SQLITE_NOMEM
|
||
+ || sqlite3_bind_text(pStmt, 2, zHash2, -1, SQLITE_STATIC)==SQLITE_NOMEM
|
||
+ ){
|
||
+ p->rc = SQLITE_NOMEM;
|
||
+ return;
|
||
+ }
|
||
+#if SPELLFIX_MX_RUN>1
|
||
+ for(i=0; i<pCur->nRow; i++){
|
||
+ if( pCur->a[i].iScore>iWorst ){
|
||
+ iWorst = pCur->a[i].iScore;
|
||
+ idxWorst = i;
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||
+ int iMatchlen = -1;
|
||
+ iRank = sqlite3_column_int(pStmt, 2);
|
||
+ if( p->pMatchStr3 ){
|
||
+ int nWord = sqlite3_column_bytes(pStmt, 1);
|
||
+ zWord = (const char*)sqlite3_column_text(pStmt, 1);
|
||
+ iDist = editDist3Core(p->pMatchStr3, zWord, nWord, p->pLang, &iMatchlen);
|
||
+ }else{
|
||
+ zK1 = (const char*)sqlite3_column_text(pStmt, 3);
|
||
+ if( zK1==0 ) continue;
|
||
+ iDist = editdist1(p->zPattern, zK1, 0);
|
||
+ }
|
||
+ if( iDist<0 ){
|
||
+ p->rc = SQLITE_NOMEM;
|
||
+ break;
|
||
+ }
|
||
+ pCur->nSearch++;
|
||
+ iScore = spellfix1Score(iDist,iRank);
|
||
+ if( p->iMaxDist>=0 ){
|
||
+ if( iDist>p->iMaxDist ) continue;
|
||
+ if( pCur->nRow>=pCur->nAlloc-1 ){
|
||
+ spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10);
|
||
+ if( pCur->a==0 ) break;
|
||
+ }
|
||
+ idx = pCur->nRow;
|
||
+ }else if( pCur->nRow<pCur->nAlloc ){
|
||
+ idx = pCur->nRow;
|
||
+ }else if( iScore<iWorst ){
|
||
+ idx = idxWorst;
|
||
+ sqlite3_free(pCur->a[idx].zWord);
|
||
+ }else{
|
||
+ continue;
|
||
+ }
|
||
+ pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
||
+ if( pCur->a[idx].zWord==0 ){
|
||
+ p->rc = SQLITE_NOMEM;
|
||
+ break;
|
||
+ }
|
||
+ pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0);
|
||
+ pCur->a[idx].iRank = iRank;
|
||
+ pCur->a[idx].iDistance = iDist;
|
||
+ pCur->a[idx].iScore = iScore;
|
||
+ pCur->a[idx].iMatchlen = iMatchlen;
|
||
+ memcpy(pCur->a[idx].zHash, zHash1, iScope+1);
|
||
+ if( pCur->nRow<pCur->nAlloc ) pCur->nRow++;
|
||
+ if( pCur->nRow==pCur->nAlloc ){
|
||
+ iWorst = pCur->a[0].iScore;
|
||
+ idxWorst = 0;
|
||
+ for(i=1; i<pCur->nRow; i++){
|
||
+ iScore = pCur->a[i].iScore;
|
||
+ if( iWorst<iScore ){
|
||
+ iWorst = iScore;
|
||
+ idxWorst = i;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ rc = sqlite3_reset(pStmt);
|
||
+ if( rc ) p->rc = rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This version of the xFilter method work if the MATCH term is present
|
||
+** and we are doing a scan.
|
||
+*/
|
||
+static int spellfix1FilterForMatch(
|
||
+ spellfix1_cursor *pCur,
|
||
+ int idxNum,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zMatchThis; /* RHS of the MATCH operator */
|
||
+ EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */
|
||
+ char *zPattern; /* Transliteration of zMatchThis */
|
||
+ int nPattern; /* Length of zPattern */
|
||
+ int iLimit = 20; /* Max number of rows of output */
|
||
+ int iScope = 3; /* Use this many characters of zClass */
|
||
+ int iLang = 0; /* Language code */
|
||
+ char *zSql; /* SQL of shadow table query */
|
||
+ sqlite3_stmt *pStmt = 0; /* Shadow table query */
|
||
+ int rc; /* Result code */
|
||
+ int idx = 1; /* Next available filter parameter */
|
||
+ spellfix1_vtab *p = pCur->pVTab; /* The virtual table that owns pCur */
|
||
+ MatchQuery x; /* For passing info to RunQuery() */
|
||
+
|
||
+ /* Load the cost table if we have not already done so */
|
||
+ if( p->zCostTable!=0 && p->pConfig3==0 ){
|
||
+ p->pConfig3 = sqlite3_malloc( sizeof(p->pConfig3[0]) );
|
||
+ if( p->pConfig3==0 ) return SQLITE_NOMEM;
|
||
+ memset(p->pConfig3, 0, sizeof(p->pConfig3[0]));
|
||
+ rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable);
|
||
+ if( rc ) return rc;
|
||
+ }
|
||
+ memset(&x, 0, sizeof(x));
|
||
+ x.iScope = 3; /* Default scope if none specified by "WHERE scope=N" */
|
||
+ x.iMaxDist = -1; /* Maximum allowed edit distance */
|
||
+
|
||
+ if( idxNum&2 ){
|
||
+ iLang = sqlite3_value_int(argv[idx++]);
|
||
+ }
|
||
+ if( idxNum&4 ){
|
||
+ iLimit = sqlite3_value_int(argv[idx++]);
|
||
+ if( iLimit<1 ) iLimit = 1;
|
||
+ }
|
||
+ if( idxNum&8 ){
|
||
+ x.iScope = sqlite3_value_int(argv[idx++]);
|
||
+ if( x.iScope<1 ) x.iScope = 1;
|
||
+ if( x.iScope>SPELLFIX_MX_HASH-2 ) x.iScope = SPELLFIX_MX_HASH-2;
|
||
+ }
|
||
+ if( idxNum&(16|32) ){
|
||
+ x.iMaxDist = sqlite3_value_int(argv[idx++]);
|
||
+ if( idxNum&16 ) x.iMaxDist--;
|
||
+ if( x.iMaxDist<0 ) x.iMaxDist = 0;
|
||
+ }
|
||
+ spellfix1ResetCursor(pCur);
|
||
+ spellfix1ResizeCursor(pCur, iLimit);
|
||
+ zMatchThis = sqlite3_value_text(argv[0]);
|
||
+ if( zMatchThis==0 ) return SQLITE_OK;
|
||
+ if( p->pConfig3 ){
|
||
+ x.pLang = editDist3FindLang(p->pConfig3, iLang);
|
||
+ pMatchStr3 = editDist3FromStringNew(x.pLang, (const char*)zMatchThis, -1);
|
||
+ if( pMatchStr3==0 ){
|
||
+ x.rc = SQLITE_NOMEM;
|
||
+ goto filter_exit;
|
||
+ }
|
||
+ }else{
|
||
+ x.pLang = 0;
|
||
+ }
|
||
+ zPattern = (char*)transliterate(zMatchThis, sqlite3_value_bytes(argv[0]));
|
||
+ sqlite3_free(pCur->zPattern);
|
||
+ pCur->zPattern = zPattern;
|
||
+ if( zPattern==0 ){
|
||
+ x.rc = SQLITE_NOMEM;
|
||
+ goto filter_exit;
|
||
+ }
|
||
+ nPattern = (int)strlen(zPattern);
|
||
+ if( zPattern[nPattern-1]=='*' ) nPattern--;
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT id, word, rank, k1"
|
||
+ " FROM \"%w\".\"%w_vocab\""
|
||
+ " WHERE langid=%d AND k2>=?1 AND k2<?2",
|
||
+ p->zDbName, p->zTableName, iLang
|
||
+ );
|
||
+ if( zSql==0 ){
|
||
+ x.rc = SQLITE_NOMEM;
|
||
+ pStmt = 0;
|
||
+ goto filter_exit;
|
||
+ }
|
||
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ pCur->iLang = iLang;
|
||
+ x.pCur = pCur;
|
||
+ x.pStmt = pStmt;
|
||
+ x.zPattern = zPattern;
|
||
+ x.nPattern = nPattern;
|
||
+ x.pMatchStr3 = pMatchStr3;
|
||
+ x.iLang = iLang;
|
||
+ x.rc = rc;
|
||
+ x.pConfig3 = p->pConfig3;
|
||
+ if( x.rc==SQLITE_OK ){
|
||
+ spellfix1RunQuery(&x, zPattern, nPattern);
|
||
+ }
|
||
+
|
||
+ if( pCur->a ){
|
||
+ qsort(pCur->a, pCur->nRow, sizeof(pCur->a[0]), spellfix1RowCompare);
|
||
+ pCur->iTop = iLimit;
|
||
+ pCur->iScope = iScope;
|
||
+ }else{
|
||
+ x.rc = SQLITE_NOMEM;
|
||
+ }
|
||
+
|
||
+filter_exit:
|
||
+ sqlite3_finalize(pStmt);
|
||
+ editDist3FromStringDelete(pMatchStr3);
|
||
+ return x.rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** This version of xFilter handles a full-table scan case
|
||
+*/
|
||
+static int spellfix1FilterForFullScan(
|
||
+ spellfix1_cursor *pCur,
|
||
+ int idxNum,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ char *zSql;
|
||
+ spellfix1_vtab *pVTab = pCur->pVTab;
|
||
+ spellfix1ResetCursor(pCur);
|
||
+ assert( idxNum==0 || idxNum==64 );
|
||
+ zSql = sqlite3_mprintf(
|
||
+ "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s",
|
||
+ pVTab->zDbName, pVTab->zTableName,
|
||
+ ((idxNum & 64) ? " WHERE rowid=?" : "")
|
||
+ );
|
||
+ if( zSql==0 ) return SQLITE_NOMEM;
|
||
+ rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0);
|
||
+ sqlite3_free(zSql);
|
||
+ if( rc==SQLITE_OK && (idxNum & 64) ){
|
||
+ assert( argc==1 );
|
||
+ rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]);
|
||
+ }
|
||
+ pCur->nRow = pCur->iRow = 0;
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_step(pCur->pFullScan);
|
||
+ if( rc==SQLITE_ROW ){ pCur->iRow = -1; rc = SQLITE_OK; }
|
||
+ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; }
|
||
+ }else{
|
||
+ pCur->iRow = 0;
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Called to "rewind" a cursor back to the beginning so that
|
||
+** it starts its output over again. Always called at least once
|
||
+** prior to any spellfix1Column, spellfix1Rowid, or spellfix1Eof call.
|
||
+*/
|
||
+static int spellfix1Filter(
|
||
+ sqlite3_vtab_cursor *cur,
|
||
+ int idxNum, const char *idxStr,
|
||
+ int argc, sqlite3_value **argv
|
||
+){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
||
+ int rc;
|
||
+ if( idxNum & 1 ){
|
||
+ rc = spellfix1FilterForMatch(pCur, idxNum, argc, argv);
|
||
+ }else{
|
||
+ rc = spellfix1FilterForFullScan(pCur, idxNum, argc, argv);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Advance a cursor to its next row of output
|
||
+*/
|
||
+static int spellfix1Next(sqlite3_vtab_cursor *cur){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
||
+ int rc = SQLITE_OK;
|
||
+ if( pCur->iRow < pCur->nRow ){
|
||
+ if( pCur->pFullScan ){
|
||
+ rc = sqlite3_step(pCur->pFullScan);
|
||
+ if( rc!=SQLITE_ROW ) pCur->iRow = pCur->nRow;
|
||
+ if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||
+ }else{
|
||
+ pCur->iRow++;
|
||
+ }
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE if we are at the end-of-file
|
||
+*/
|
||
+static int spellfix1Eof(sqlite3_vtab_cursor *cur){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor *)cur;
|
||
+ return pCur->iRow>=pCur->nRow;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return columns from the current row.
|
||
+*/
|
||
+static int spellfix1Column(
|
||
+ sqlite3_vtab_cursor *cur,
|
||
+ sqlite3_context *ctx,
|
||
+ int i
|
||
+){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor*)cur;
|
||
+ if( pCur->pFullScan ){
|
||
+ if( i<=SPELLFIX_COL_LANGID ){
|
||
+ sqlite3_result_value(ctx, sqlite3_column_value(pCur->pFullScan, i));
|
||
+ }else{
|
||
+ sqlite3_result_null(ctx);
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ switch( i ){
|
||
+ case SPELLFIX_COL_WORD: {
|
||
+ sqlite3_result_text(ctx, pCur->a[pCur->iRow].zWord, -1, SQLITE_STATIC);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_RANK: {
|
||
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iRank);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_DISTANCE: {
|
||
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iDistance);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_LANGID: {
|
||
+ sqlite3_result_int(ctx, pCur->iLang);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_SCORE: {
|
||
+ sqlite3_result_int(ctx, pCur->a[pCur->iRow].iScore);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_MATCHLEN: {
|
||
+ int iMatchlen = pCur->a[pCur->iRow].iMatchlen;
|
||
+ if( iMatchlen<0 ){
|
||
+ int nPattern = (int)strlen(pCur->zPattern);
|
||
+ char *zWord = pCur->a[pCur->iRow].zWord;
|
||
+ int nWord = (int)strlen(zWord);
|
||
+
|
||
+ if( nPattern>0 && pCur->zPattern[nPattern-1]=='*' ){
|
||
+ char *zTranslit;
|
||
+ int res;
|
||
+ zTranslit = (char *)transliterate((unsigned char *)zWord, nWord);
|
||
+ if( !zTranslit ) return SQLITE_NOMEM;
|
||
+ res = editdist1(pCur->zPattern, zTranslit, &iMatchlen);
|
||
+ sqlite3_free(zTranslit);
|
||
+ if( res<0 ) return SQLITE_NOMEM;
|
||
+ iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen);
|
||
+ }else{
|
||
+ iMatchlen = utf8Charlen(zWord, nWord);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ sqlite3_result_int(ctx, iMatchlen);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_PHONEHASH: {
|
||
+ sqlite3_result_text(ctx, pCur->a[pCur->iRow].zHash, -1, SQLITE_STATIC);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_TOP: {
|
||
+ sqlite3_result_int(ctx, pCur->iTop);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_SCOPE: {
|
||
+ sqlite3_result_int(ctx, pCur->iScope);
|
||
+ break;
|
||
+ }
|
||
+ case SPELLFIX_COL_SRCHCNT: {
|
||
+ sqlite3_result_int(ctx, pCur->nSearch);
|
||
+ break;
|
||
+ }
|
||
+ default: {
|
||
+ sqlite3_result_null(ctx);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The rowid.
|
||
+*/
|
||
+static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||
+ spellfix1_cursor *pCur = (spellfix1_cursor*)cur;
|
||
+ if( pCur->pFullScan ){
|
||
+ *pRowid = sqlite3_column_int64(pCur->pFullScan, 4);
|
||
+ }else{
|
||
+ *pRowid = pCur->a[pCur->iRow].iRowid;
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The xUpdate() method.
|
||
+*/
|
||
+static int spellfix1Update(
|
||
+ sqlite3_vtab *pVTab,
|
||
+ int argc,
|
||
+ sqlite3_value **argv,
|
||
+ sqlite_int64 *pRowid
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ sqlite3_int64 rowid, newRowid;
|
||
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
||
+ sqlite3 *db = p->db;
|
||
+
|
||
+ if( argc==1 ){
|
||
+ /* A delete operation on the rowid given by argv[0] */
|
||
+ rowid = *pRowid = sqlite3_value_int64(argv[0]);
|
||
+ spellfix1DbExec(&rc, db, "DELETE FROM \"%w\".\"%w_vocab\" "
|
||
+ " WHERE id=%lld",
|
||
+ p->zDbName, p->zTableName, rowid);
|
||
+ }else{
|
||
+ const unsigned char *zWord = sqlite3_value_text(argv[SPELLFIX_COL_WORD+2]);
|
||
+ int nWord = sqlite3_value_bytes(argv[SPELLFIX_COL_WORD+2]);
|
||
+ int iLang = sqlite3_value_int(argv[SPELLFIX_COL_LANGID+2]);
|
||
+ int iRank = sqlite3_value_int(argv[SPELLFIX_COL_RANK+2]);
|
||
+ const unsigned char *zSoundslike =
|
||
+ sqlite3_value_text(argv[SPELLFIX_COL_SOUNDSLIKE+2]);
|
||
+ int nSoundslike = sqlite3_value_bytes(argv[SPELLFIX_COL_SOUNDSLIKE+2]);
|
||
+ char *zK1, *zK2;
|
||
+ int i;
|
||
+ char c;
|
||
+
|
||
+ if( zWord==0 ){
|
||
+ /* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy');
|
||
+ ** cause zWord to be NULL, so we look at the "command" column to see
|
||
+ ** what special actions to take */
|
||
+ const char *zCmd =
|
||
+ (const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]);
|
||
+ if( zCmd==0 ){
|
||
+ pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word",
|
||
+ p->zTableName);
|
||
+ return SQLITE_CONSTRAINT_NOTNULL;
|
||
+ }
|
||
+ if( strcmp(zCmd,"reset")==0 ){
|
||
+ /* Reset the edit cost table (if there is one). */
|
||
+ editDist3ConfigDelete(p->pConfig3);
|
||
+ p->pConfig3 = 0;
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ if( strncmp(zCmd,"edit_cost_table=",16)==0 ){
|
||
+ editDist3ConfigDelete(p->pConfig3);
|
||
+ p->pConfig3 = 0;
|
||
+ sqlite3_free(p->zCostTable);
|
||
+ p->zCostTable = spellfix1Dequote(zCmd+16);
|
||
+ if( p->zCostTable==0 ) return SQLITE_NOMEM;
|
||
+ if( p->zCostTable[0]==0 || sqlite3_stricmp(p->zCostTable,"null")==0 ){
|
||
+ sqlite3_free(p->zCostTable);
|
||
+ p->zCostTable = 0;
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ pVTab->zErrMsg = sqlite3_mprintf("unknown value for %s.command: \"%w\"",
|
||
+ p->zTableName, zCmd);
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ if( iRank<1 ) iRank = 1;
|
||
+ if( zSoundslike ){
|
||
+ zK1 = (char*)transliterate(zSoundslike, nSoundslike);
|
||
+ }else{
|
||
+ zK1 = (char*)transliterate(zWord, nWord);
|
||
+ }
|
||
+ if( zK1==0 ) return SQLITE_NOMEM;
|
||
+ for(i=0; (c = zK1[i])!=0; i++){
|
||
+ if( c>='A' && c<='Z' ) zK1[i] += 'a' - 'A';
|
||
+ }
|
||
+ zK2 = (char*)phoneticHash((const unsigned char*)zK1, i);
|
||
+ if( zK2==0 ){
|
||
+ sqlite3_free(zK1);
|
||
+ return SQLITE_NOMEM;
|
||
+ }
|
||
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
|
||
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ){
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) "
|
||
+ "VALUES(%d,%d,%Q,%Q,%Q)",
|
||
+ p->zDbName, p->zTableName,
|
||
+ iRank, iLang, zWord, zK1, zK2
|
||
+ );
|
||
+ }else{
|
||
+ newRowid = sqlite3_value_int64(argv[1]);
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "INSERT INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) "
|
||
+ "VALUES(%lld,%d,%d,%Q,%Q,%Q)",
|
||
+ p->zDbName, p->zTableName,
|
||
+ newRowid, iRank, iLang, zWord, zK1, zK2
|
||
+ );
|
||
+ }
|
||
+ *pRowid = sqlite3_last_insert_rowid(db);
|
||
+ }else{
|
||
+ rowid = sqlite3_value_int64(argv[0]);
|
||
+ newRowid = *pRowid = sqlite3_value_int64(argv[1]);
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "UPDATE \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d,"
|
||
+ " word=%Q, k1=%Q, k2=%Q WHERE id=%lld",
|
||
+ p->zDbName, p->zTableName, newRowid, iRank, iLang,
|
||
+ zWord, zK1, zK2, rowid
|
||
+ );
|
||
+ }
|
||
+ sqlite3_free(zK1);
|
||
+ sqlite3_free(zK2);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Rename the spellfix1 table.
|
||
+*/
|
||
+static int spellfix1Rename(sqlite3_vtab *pVTab, const char *zNew){
|
||
+ spellfix1_vtab *p = (spellfix1_vtab*)pVTab;
|
||
+ sqlite3 *db = p->db;
|
||
+ int rc = SQLITE_OK;
|
||
+ char *zNewName = sqlite3_mprintf("%s", zNew);
|
||
+ if( zNewName==0 ){
|
||
+ return SQLITE_NOMEM;
|
||
+ }
|
||
+ spellfix1DbExec(&rc, db,
|
||
+ "ALTER TABLE \"%w\".\"%w_vocab\" RENAME TO \"%w_vocab\"",
|
||
+ p->zDbName, p->zTableName, zNewName
|
||
+ );
|
||
+ if( rc==SQLITE_OK ){
|
||
+ sqlite3_free(p->zTableName);
|
||
+ p->zTableName = zNewName;
|
||
+ }else{
|
||
+ sqlite3_free(zNewName);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** A virtual table module that provides fuzzy search.
|
||
+*/
|
||
+static sqlite3_module spellfix1Module = {
|
||
+ 0, /* iVersion */
|
||
+ spellfix1Create, /* xCreate - handle CREATE VIRTUAL TABLE */
|
||
+ spellfix1Connect, /* xConnect - reconnected to an existing table */
|
||
+ spellfix1BestIndex, /* xBestIndex - figure out how to do a query */
|
||
+ spellfix1Disconnect, /* xDisconnect - close a connection */
|
||
+ spellfix1Destroy, /* xDestroy - handle DROP TABLE */
|
||
+ spellfix1Open, /* xOpen - open a cursor */
|
||
+ spellfix1Close, /* xClose - close a cursor */
|
||
+ spellfix1Filter, /* xFilter - configure scan constraints */
|
||
+ spellfix1Next, /* xNext - advance a cursor */
|
||
+ spellfix1Eof, /* xEof - check for end of scan */
|
||
+ spellfix1Column, /* xColumn - read data */
|
||
+ spellfix1Rowid, /* xRowid - read data */
|
||
+ spellfix1Update, /* xUpdate */
|
||
+ 0, /* xBegin */
|
||
+ 0, /* xSync */
|
||
+ 0, /* xCommit */
|
||
+ 0, /* xRollback */
|
||
+ 0, /* xFindMethod */
|
||
+ spellfix1Rename, /* xRename */
|
||
+};
|
||
+
|
||
+/*
|
||
+** Register the various functions and the virtual table.
|
||
+*/
|
||
+static int spellfix1Register(sqlite3 *db){
|
||
+ int rc = SQLITE_OK;
|
||
+ int i;
|
||
+ rc = sqlite3_create_function(db, "spellfix1_translit", 1, SQLITE_UTF8, 0,
|
||
+ transliterateSqlFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "spellfix1_editdist", 2, SQLITE_UTF8, 0,
|
||
+ editdistSqlFunc, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "spellfix1_phonehash", 1, SQLITE_UTF8, 0,
|
||
+ phoneticHashSqlFunc, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "spellfix1_scriptcode", 1, SQLITE_UTF8, 0,
|
||
+ scriptCodeSqlFunc, 0, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_module(db, "spellfix1", &spellfix1Module, 0);
|
||
+ }
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = editDist3Install(db);
|
||
+ }
|
||
+
|
||
+ /* Verify sanity of the translit[] table */
|
||
+ for(i=0; i<sizeof(translit)/sizeof(translit[0])-1; i++){
|
||
+ assert( translit[i].cFrom<translit[i+1].cFrom );
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+/*
|
||
+** Extension load function.
|
||
+*/
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_spellfix_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ return spellfix1Register(db);
|
||
+#endif
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ return spellfix1Register(db);
|
||
+#endif
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/sqlite3.1 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/sqlite3.1 2015-01-31 00:31:56.499149100 +0100
|
||
@@ -2,7 +2,7 @@
|
||
.\" First parameter, NAME, should be all caps
|
||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||
.\" other parameters are allowed: see man(7), man(1)
|
||
-.TH SQLITE3 1 "Fri Oct 31 10:41:31 EDT 2014"
|
||
+.TH SQLITE3 1 "Fri Jan 9 10:13:00 EDT 2015"
|
||
.\" Please adjust this date whenever revising the manpage.
|
||
.\"
|
||
.\" Some roff macros, for reference:
|
||
@@ -130,12 +130,13 @@ sqlite>
|
||
%load FILE ?ENTRY? Load an extension library
|
||
%log FILE|off Turn logging on or off. FILE can be stderr/stdout
|
||
%mode MODE ?TABLE? Set output mode where MODE is one of:
|
||
+ ascii Columns/rows delimited by 0x1F and 0x1E
|
||
csv Comma-separated values
|
||
column Left-aligned columns. (See .width)
|
||
html HTML <table> code
|
||
insert SQL insert statements for TABLE
|
||
line One value per line
|
||
- list Values delimited by .separator string
|
||
+ list Values delimited by .separator strings
|
||
tabs Tab-separated values
|
||
tcl TCL list elements
|
||
%nullvalue STRING Use STRING in place of NULL values
|
||
@@ -151,8 +152,8 @@ sqlite>
|
||
%schema ?TABLE? Show the CREATE statements
|
||
If TABLE specified, only show tables matching
|
||
LIKE pattern TABLE.
|
||
-%separator STRING ?NL? Change separator used by output mode and .import
|
||
- NL is the end-of-line mark for CSV
|
||
+%separator COL ?ROW? Change the column separator and optionally the row
|
||
+ separator for both the output mode and .import
|
||
%shell CMD ARGS... Run CMD ARGS... in a system shell
|
||
%show Show the current values for various settings
|
||
%stats on|off Turn stats on or off
|
||
@@ -173,6 +174,9 @@ sqlite>
|
||
.B sqlite3
|
||
has the following options:
|
||
.TP
|
||
+.B \-ascii
|
||
+Set output mode to ascii.
|
||
+.TP
|
||
.B \-bail
|
||
Stop after hitting an error.
|
||
.TP
|
||
@@ -221,17 +225,41 @@ scripts or other programs
|
||
Query results will be displayed with the separator (|, by default)
|
||
character between each field value. The default.
|
||
.TP
|
||
+.BI \-lookaside\ SIZE N
|
||
+Use
|
||
+.I SIZE
|
||
+entries of
|
||
+.I N
|
||
+bytes for lookaside memory.
|
||
+.TP
|
||
.BI \-mmap\ N
|
||
Set default mmap size to
|
||
.I N
|
||
\.
|
||
.TP
|
||
+.BI \-newline\ separator
|
||
+Set output row separator. Default is the newline character.
|
||
+.TP
|
||
.BI \-nullvalue\ string
|
||
Set string used to represent NULL values. Default is ''
|
||
(empty string).
|
||
.TP
|
||
+.BI \-pagecache\ SIZE N
|
||
+Use
|
||
+.I SIZE
|
||
+entries of
|
||
+.I N
|
||
+bytes each for page cache memory.
|
||
+.TP
|
||
+.BI \-scratch\ SIZE N
|
||
+Use
|
||
+.I SIZE
|
||
+entries of
|
||
+.I N
|
||
+bytes each for scratch memory.
|
||
+.TP
|
||
.BI \-separator\ separator
|
||
-Set output field separator. Default is '|'.
|
||
+Set output column separator. Default is '|'.
|
||
.TP
|
||
.B \-stats
|
||
Print memory stats before each finalize.
|
||
@@ -243,6 +271,9 @@ Show SQLite version.
|
||
Use
|
||
.I name
|
||
as the default VFS.
|
||
+.TP
|
||
+.B \-vfslog
|
||
+Enable the vfslog extension.
|
||
|
||
|
||
.SH INIT FILE
|
||
@@ -256,12 +287,10 @@ o The default configuration is establish
|
||
|
||
.sp
|
||
.nf
|
||
-.cc |
|
||
mode = LIST
|
||
separator = "|"
|
||
main prompt = "sqlite> "
|
||
continue prompt = " ...> "
|
||
-|cc .
|
||
.sp
|
||
.fi
|
||
|
||
--- origsrc/sqlite-autoconf-3080802/sqlite3.c 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/sqlite3.c 2015-01-31 00:31:56.578153600 +0100
|
||
@@ -132,35 +132,19 @@
|
||
#endif
|
||
|
||
/*
|
||
-** For MinGW, check to see if we can include the header file containing its
|
||
-** version information, among other things. Normally, this internal MinGW
|
||
-** header file would [only] be included automatically by other MinGW header
|
||
-** files; however, the contained version information is now required by this
|
||
-** header file to work around binary compatibility issues (see below) and
|
||
-** this is the only known way to reliably obtain it. This entire #if block
|
||
-** would be completely unnecessary if there was any other way of detecting
|
||
-** MinGW via their preprocessor (e.g. if they customized their GCC to define
|
||
-** some MinGW-specific macros). When compiling for MinGW, either the
|
||
-** _HAVE_MINGW_H or _HAVE__MINGW_H (note the extra underscore) macro must be
|
||
-** defined; otherwise, detection of conditions specific to MinGW will be
|
||
-** disabled.
|
||
-*/
|
||
-#if defined(_HAVE_MINGW_H)
|
||
-# include "mingw.h"
|
||
-#elif defined(_HAVE__MINGW_H)
|
||
-# include "_mingw.h"
|
||
-#endif
|
||
-
|
||
-/*
|
||
-** For MinGW version 4.x (and higher), check to see if the _USE_32BIT_TIME_T
|
||
-** define is required to maintain binary compatibility with the MSVC runtime
|
||
+** For MinGW (any 32-bit version), check to see if the _USE_32BIT_TIME_T
|
||
+** define can be used to maintain binary compatibility with the MSVC runtime
|
||
** library in use (e.g. for Windows XP).
|
||
*/
|
||
-#if !defined(_USE_32BIT_TIME_T) && !defined(_USE_64BIT_TIME_T) && \
|
||
- defined(_WIN32) && !defined(_WIN64) && \
|
||
- defined(__MINGW_MAJOR_VERSION) && __MINGW_MAJOR_VERSION >= 4 && \
|
||
- defined(__MSVCRT__)
|
||
+#if !defined(_USE_32BIT_TIME_T) && !defined(__MINGW_USE_VC2005_COMPAT) && \
|
||
+ defined(_WIN32) && !defined(_WIN64) && defined(__MSVCRT__)
|
||
# define _USE_32BIT_TIME_T
|
||
+# ifndef NTDDI_VERSION
|
||
+# define NTDDI_VERSION NTDDI_WINXPSP1
|
||
+# endif
|
||
+# ifndef _WIN32_WINNT
|
||
+# define _WIN32_WINNT _WIN32_WINNT_WINXP
|
||
+# endif
|
||
#endif
|
||
|
||
/* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear
|
||
@@ -4328,6 +4312,8 @@ SQLITE_API int sqlite3_create_function(
|
||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||
void (*xFinal)(sqlite3_context*)
|
||
);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
||
SQLITE_API int sqlite3_create_function16(
|
||
sqlite3 *db,
|
||
const void *zFunctionName,
|
||
@@ -4813,6 +4799,8 @@ SQLITE_API int sqlite3_create_collation(
|
||
void *pArg,
|
||
int(*xCompare)(void*,int,const void*,int,const void*)
|
||
);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
||
SQLITE_API int sqlite3_create_collation_v2(
|
||
sqlite3*,
|
||
const char *zName,
|
||
@@ -7453,6 +7441,8 @@ SQLITE_API int sqlite3_wal_autocheckpoin
|
||
** complication) of [sqlite3_wal_checkpoint_v2()].
|
||
*/
|
||
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0);
|
||
|
||
/*
|
||
** CAPI3REF: Checkpoint a database
|
||
@@ -8107,6 +8097,13 @@ struct sqlite3_rtree_query_info {
|
||
# define SQLITE_MAX_TRIGGER_DEPTH 1000
|
||
#endif
|
||
|
||
+/*
|
||
+** Maximum supported path-length..
|
||
+*/
|
||
+#ifndef SQLITE_MAX_PATH_LENGTH
|
||
+# define SQLITE_MAX_PATH_LENGTH 1024
|
||
+#endif
|
||
+
|
||
/************** End of sqliteLimit.h *****************************************/
|
||
/************** Continuing where we left off in sqliteInt.h ******************/
|
||
|
||
@@ -8941,7 +8938,7 @@ SQLITE_PRIVATE const int sqlite3one;
|
||
#endif
|
||
#ifndef SQLITE_MAX_MMAP_SIZE
|
||
# if defined(__linux__) \
|
||
- || defined(_WIN32) \
|
||
+ || defined(_WIN32) || defined(__CYGWIN__) \
|
||
|| (defined(__APPLE__) && defined(__MACH__)) \
|
||
|| defined(__sun)
|
||
# define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */
|
||
@@ -9477,7 +9474,7 @@ struct VdbeOp {
|
||
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
|
||
int (*xAdvance)(BtCursor *, int *);
|
||
} p4;
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
char *zComment; /* Comment to improve readability */
|
||
#endif
|
||
#ifdef VDBE_PROFILE
|
||
@@ -9840,7 +9837,7 @@ SQLITE_PRIVATE void sqlite3VdbeLinkSubPr
|
||
** comments in VDBE programs that show key decision points in the code
|
||
** generator.
|
||
*/
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 0
|
||
SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...);
|
||
# define VdbeComment(X) sqlite3VdbeComment X
|
||
SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
|
||
@@ -9851,9 +9848,13 @@ SQLITE_PRIVATE void sqlite3VdbeNoopCom
|
||
# define VdbeModuleComment(X)
|
||
# endif
|
||
#else
|
||
-# define VdbeComment(X)
|
||
-# define VdbeNoopComment(X)
|
||
-# define VdbeModuleComment(X)
|
||
+# define VdbeComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeComment X
|
||
+# define VdbeNoopComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeNoopComment X
|
||
+# ifdef SQLITE_ENABLE_MODULE_COMMENTS
|
||
+# define VdbeModuleComment(X) if (sqlite3GlobalConfig.bVdbeComments) sqlite3VdbeNoopComment X
|
||
+#else
|
||
+# define VdbeModuleComment(X)
|
||
+#endif
|
||
#endif
|
||
|
||
/*
|
||
@@ -10361,19 +10362,21 @@ SQLITE_PRIVATE int sqlite3HeaderSizePcac
|
||
# endif
|
||
#endif
|
||
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
||
-# define SQLITE_OS_OTHER 0
|
||
-# ifndef SQLITE_OS_WIN
|
||
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
|
||
- defined(__MINGW32__) || defined(__BORLANDC__)
|
||
-# define SQLITE_OS_WIN 1
|
||
-# define SQLITE_OS_UNIX 0
|
||
-# else
|
||
-# define SQLITE_OS_WIN 0
|
||
-# define SQLITE_OS_UNIX 1
|
||
-# endif
|
||
-# else
|
||
-# define SQLITE_OS_UNIX 0
|
||
+# define SQLITE_OS_OTHER 0
|
||
+# ifndef SQLITE_OS_WIN
|
||
+# if defined(__CYGWIN__)
|
||
+# define SQLITE_OS_WIN 1
|
||
+# define SQLITE_OS_UNIX 1
|
||
+# elif defined(_WIN32) || defined(WIN32) || defined(__MSVCRT__) || defined(__BORLANDC__)
|
||
+# define SQLITE_OS_WIN 1
|
||
+# define SQLITE_OS_UNIX 0
|
||
+# else
|
||
+# define SQLITE_OS_WIN 0
|
||
+# define SQLITE_OS_UNIX 1
|
||
# endif
|
||
+# else
|
||
+# define SQLITE_OS_UNIX 0
|
||
+# endif
|
||
#else
|
||
# ifndef SQLITE_OS_WIN
|
||
# define SQLITE_OS_WIN 0
|
||
@@ -10614,7 +10617,7 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sq
|
||
# define SQLITE_MUTEX_OMIT
|
||
#endif
|
||
#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP)
|
||
-# if SQLITE_OS_UNIX
|
||
+# if SQLITE_OS_UNIX || (defined(__CYGWIN__) && !defined(SQLITE_TEST))
|
||
# define SQLITE_MUTEX_PTHREADS
|
||
# elif SQLITE_OS_WIN
|
||
# define SQLITE_MUTEX_W32
|
||
@@ -12613,6 +12616,7 @@ struct Sqlite3Config {
|
||
sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
|
||
void (*xLog)(void*,int,const char*); /* Function for logging */
|
||
void *pLogArg; /* First argument to xLog() */
|
||
+ int bVdbeComments; /* True to enable VDBE comments */
|
||
#ifdef SQLITE_ENABLE_SQLLOG
|
||
void(*xSqllog)(void*,sqlite3*,const char*, int);
|
||
void *pSqllogArg;
|
||
@@ -13785,6 +13789,11 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3
|
||
0, /* pInitMutex */
|
||
0, /* xLog */
|
||
0, /* pLogArg */
|
||
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+ 1, /* bVdbeComments */
|
||
+#else
|
||
+ 0, /* bVdbeComments */
|
||
+#endif
|
||
#ifdef SQLITE_ENABLE_SQLLOG
|
||
0, /* xSqllog */
|
||
0, /* pSqllogArg */
|
||
@@ -14006,6 +14015,9 @@ static const char * const azCompileOpt[]
|
||
#ifdef SQLITE_MAX_SCHEMA_RETRY
|
||
"MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
|
||
#endif
|
||
+#ifdef SQLITE_MAX_WORKER_THREADS
|
||
+ "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS),
|
||
+#endif
|
||
#if SQLITE_MEMDEBUG
|
||
"MEMDEBUG",
|
||
#endif
|
||
@@ -15233,7 +15245,7 @@ static int parseHhMmSs(const char *zDate
|
||
zDate += 5;
|
||
if( *zDate==':' ){
|
||
zDate++;
|
||
- if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
|
||
+ if( getDigits(zDate, 2, 0, 60, 0, &s)!=1 ){
|
||
return 1;
|
||
}
|
||
zDate += 2;
|
||
@@ -16831,7 +16843,7 @@ static malloc_zone_t* _sqliteZone_;
|
||
** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
|
||
** the malloc.h header file.
|
||
*/
|
||
-#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
||
+#elif (defined(_MSC_VER) || defined(__MSVCRT__)) && !defined(SQLITE_WITHOUT_MSIZE)
|
||
# define SQLITE_USE_MALLOC_H
|
||
# define SQLITE_USE_MSIZE
|
||
#endif
|
||
@@ -19821,6 +19833,12 @@ SQLITE_API int sqlite3_open_file_count =
|
||
#ifdef __CYGWIN__
|
||
# include <sys/cygwin.h>
|
||
# include <errno.h> /* amalgamator: dontcache */
|
||
+# include <unistd.h> /* amalgamator: dontcache */
|
||
+#elif defined(_WIN32)
|
||
+enum {
|
||
+ CCP_POSIX_TO_WIN_W = 1, /* from is char*, to is wchar_t* */
|
||
+ CCP_RELATIVE = 0x100 /* Request to keep path relative. */
|
||
+};
|
||
#endif
|
||
|
||
/*
|
||
@@ -19889,8 +19907,8 @@ SQLITE_API int sqlite3_open_file_count =
|
||
*/
|
||
struct sqlite3_mutex {
|
||
CRITICAL_SECTION mutex; /* Mutex controlling the lock */
|
||
- int id; /* Mutex type */
|
||
#ifdef SQLITE_DEBUG
|
||
+ int id; /* Mutex type */
|
||
volatile int nRef; /* Number of enterances */
|
||
volatile DWORD owner; /* Thread holding this mutex */
|
||
volatile int trace; /* True to trace changes */
|
||
@@ -19908,7 +19926,7 @@ struct sqlite3_mutex {
|
||
#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
|
||
0L, (DWORD)0, 0 }
|
||
#else
|
||
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
|
||
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER }
|
||
#endif
|
||
|
||
#ifdef SQLITE_DEBUG
|
||
@@ -19946,7 +19964,6 @@ static sqlite3_mutex winMutex_staticMute
|
||
};
|
||
|
||
static int winMutex_isInit = 0;
|
||
-static int winMutex_isNt = -1; /* <0 means "need to query" */
|
||
|
||
/* As the winMutexInit() and winMutexEnd() functions are called as part
|
||
** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
|
||
@@ -19954,7 +19971,9 @@ static int winMutex_isNt = -1; /* <0 mea
|
||
*/
|
||
static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
|
||
|
||
-SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */
|
||
+#if defined(__CYGWIN__) && defined(SQLITE_AMALGAMATION)
|
||
+static
|
||
+#endif
|
||
SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
|
||
|
||
static int winMutexInit(void){
|
||
@@ -20146,30 +20165,15 @@ static int winMutexTry(sqlite3_mutex *p)
|
||
** The sqlite3_mutex_try() routine is very rarely used, and when it
|
||
** is used it is merely an optimization. So it is OK for it to always
|
||
** fail.
|
||
- **
|
||
- ** The TryEnterCriticalSection() interface is only available on WinNT.
|
||
- ** And some windows compilers complain if you try to use it without
|
||
- ** first doing some #defines that prevent SQLite from building on Win98.
|
||
- ** For that reason, we will omit this optimization for now. See
|
||
- ** ticket #2685.
|
||
*/
|
||
-#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
|
||
assert( winMutex_isInit==1 );
|
||
- assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
|
||
- if( winMutex_isNt<0 ){
|
||
- winMutex_isNt = sqlite3_win32_is_nt();
|
||
- }
|
||
- assert( winMutex_isNt==0 || winMutex_isNt==1 );
|
||
- if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
|
||
+ if( TryEnterCriticalSection(&p->mutex) ){
|
||
#ifdef SQLITE_DEBUG
|
||
p->owner = tid;
|
||
p->nRef++;
|
||
#endif
|
||
rc = SQLITE_OK;
|
||
}
|
||
-#else
|
||
- UNUSED_PARAMETER(p);
|
||
-#endif
|
||
#ifdef SQLITE_DEBUG
|
||
if( p->trace ){
|
||
OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
|
||
@@ -21262,13 +21266,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
|
||
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
|
||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||
|
||
-#ifdef SQLITE_ENABLE_API_ARMOR
|
||
- if( ap==0 ){
|
||
- (void)SQLITE_MISUSE_BKPT;
|
||
- sqlite3StrAccumReset(pAccum);
|
||
- return;
|
||
- }
|
||
-#endif
|
||
bufpt = 0;
|
||
if( bFlags ){
|
||
if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
|
||
@@ -22367,7 +22364,8 @@ SQLITE_PRIVATE void sqlite3PrngRestoreSt
|
||
#if SQLITE_MAX_WORKER_THREADS>0
|
||
|
||
/********************************* Unix Pthreads ****************************/
|
||
-#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
||
+#if (SQLITE_OS_UNIX || defined(__CYGWIN__)) && \
|
||
+ defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
||
|
||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||
/* #include <pthread.h> */
|
||
@@ -22430,12 +22428,14 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQL
|
||
return rc;
|
||
}
|
||
|
||
-#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
|
||
+#endif /* (SQLITE_OS_UNIX || defined(__CYGWIN__)) && \
|
||
+ defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 */
|
||
/******************************** End Unix Pthreads *************************/
|
||
|
||
|
||
/********************************* Win32 Threads ****************************/
|
||
-#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
|
||
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
||
+ SQLITE_THREADSAFE>0 && (defined(_MSC_VER) || defined(__MSVCRT__))
|
||
|
||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||
#include <process.h>
|
||
@@ -22508,7 +22508,6 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HA
|
||
/* Get the results of the thread */
|
||
SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||
DWORD rc;
|
||
- BOOL bRc;
|
||
|
||
assert( ppOut!=0 );
|
||
if( NEVER(p==0) ) return SQLITE_NOMEM;
|
||
@@ -22520,15 +22519,15 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQL
|
||
assert( p->id!=0 && p->id!=GetCurrentThreadId() );
|
||
rc = sqlite3Win32Wait((HANDLE)p->tid);
|
||
assert( rc!=WAIT_IO_COMPLETION );
|
||
- bRc = CloseHandle((HANDLE)p->tid);
|
||
- assert( bRc );
|
||
+ CloseHandle((HANDLE)p->tid);
|
||
}
|
||
if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
|
||
sqlite3_free(p);
|
||
return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
|
||
}
|
||
|
||
-#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */
|
||
+#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
||
+ SQLITE_THREADSAFE>0 && (defined(_MSC_VER) || defined(__MSVCRT__)) */
|
||
/******************************** End Win32 Threads *************************/
|
||
|
||
|
||
@@ -25046,7 +25045,7 @@ SQLITE_PRIVATE const char *sqlite3Opcode
|
||
# include <sys/mman.h>
|
||
#endif
|
||
|
||
-#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
|
||
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS || defined(HAVE_FLOCK)
|
||
# include <sys/ioctl.h>
|
||
# if OS_VXWORKS
|
||
# include <semaphore.h>
|
||
@@ -25096,7 +25095,7 @@ SQLITE_PRIVATE const char *sqlite3Opcode
|
||
/*
|
||
** Maximum supported path-length.
|
||
*/
|
||
-#define MAX_PATHNAME 512
|
||
+#define MAX_PATHNAME SQLITE_MAX_PATH_LENGTH
|
||
|
||
/*
|
||
** Only set the lastErrno if the error code is a real error and not
|
||
@@ -25499,6 +25498,7 @@ static int unixGetpagesize(void);
|
||
** testing and sandboxing. The following array holds the names and pointers
|
||
** to all overrideable system calls.
|
||
*/
|
||
+#define aSyscall aUnixSyscall
|
||
static struct unix_syscall {
|
||
const char *zName; /* Name of the system call */
|
||
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
|
||
@@ -27392,7 +27392,7 @@ static int dotlockClose(sqlite3_file *id
|
||
** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if
|
||
** compiling for VXWORKS.
|
||
*/
|
||
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
||
+#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS || defined(HAVE_FLOCK)
|
||
|
||
/*
|
||
** Retry flock() calls that fail with EINTR
|
||
@@ -28535,9 +28535,13 @@ static int unixWrite(
|
||
** Count the number of fullsyncs and normal syncs. This is used to test
|
||
** that syncs and fullsyncs are occurring at the right times.
|
||
*/
|
||
+#if SQLITE_OS_WIN
|
||
+extern int sqlite3_sync_count, sqlite3_fullsync_count;
|
||
+#else
|
||
SQLITE_API int sqlite3_sync_count = 0;
|
||
SQLITE_API int sqlite3_fullsync_count = 0;
|
||
#endif
|
||
+#endif
|
||
|
||
/*
|
||
** We do not trust systems to provide a working fdatasync(). Some do.
|
||
@@ -30177,7 +30181,7 @@ IOMETHODS(
|
||
0 /* xShmMap method */
|
||
)
|
||
|
||
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
||
+#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS || defined(HAVE_FLOCK)
|
||
IOMETHODS(
|
||
flockIoFinder, /* Finder function name */
|
||
flockIoMethods, /* sqlite3_io_methods object name */
|
||
@@ -30216,6 +30220,19 @@ IOMETHODS(
|
||
)
|
||
#endif
|
||
|
||
+#if defined(__CYGWIN__) && defined(HAVE_FLOCK)
|
||
+IOMETHODS(
|
||
+ cygwinIoFinder, /* Finder function name */
|
||
+ cygwinIoMethods, /* sqlite3_io_methods object name */
|
||
+ 1, /* shared memory is disabled */
|
||
+ flockClose, /* xClose method */
|
||
+ flockLock, /* xLock method */
|
||
+ flockUnlock, /* xUnlock method */
|
||
+ flockCheckReservedLock, /* xCheckReservedLock method */
|
||
+ 0 /* xShmMap method */
|
||
+)
|
||
+#endif
|
||
+
|
||
/*
|
||
** The proxy locking method is a "super-method" in the sense that it
|
||
** opens secondary file descriptors for the conch and lock files and
|
||
@@ -30539,6 +30556,15 @@ static int fillInUnixFile(
|
||
unixLeaveMutex();
|
||
}
|
||
#endif
|
||
+
|
||
+#if defined(__CYGWIN__) && defined(HAVE_FLOCK)
|
||
+ else if( pLockingStyle == &cygwinIoMethods ){
|
||
+ if ((osFcntl(h, F_LCK_MANDATORY, 1) != 0) && (errno != EINVAL)) {
|
||
+ /* The API exists but it refused to enable mandatory locking! */
|
||
+ rc = SQLITE_IOERR_ACCESS;
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
|
||
pNew->lastErrno = 0;
|
||
#if OS_VXWORKS
|
||
@@ -30568,6 +30594,8 @@ static const char *unixTempFileDir(void)
|
||
0,
|
||
0,
|
||
0,
|
||
+ 0,
|
||
+ 0,
|
||
"/var/tmp",
|
||
"/usr/tmp",
|
||
"/tmp",
|
||
@@ -30580,6 +30608,8 @@ static const char *unixTempFileDir(void)
|
||
azDirs[0] = sqlite3_temp_directory;
|
||
if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
|
||
if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
|
||
+ if( !azDirs[3] ) azDirs[2] = getenv("TMP");
|
||
+ if( !azDirs[4] ) azDirs[3] = getenv("TEMP");
|
||
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
||
if( zDir==0 ) continue;
|
||
if( osStat(zDir, &buf) ) continue;
|
||
@@ -31199,7 +31229,7 @@ static int unixFullPathname(
|
||
#include <dlfcn.h>
|
||
static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){
|
||
UNUSED_PARAMETER(NotUsed);
|
||
- return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL);
|
||
+ return dlopen(zFilename, RTLD_NOW | RTLD_LOCAL);
|
||
}
|
||
|
||
/*
|
||
@@ -31330,7 +31360,11 @@ static int unixSleep(sqlite3_vfs *NotUse
|
||
** sqlite3OsCurrentTime() during testing.
|
||
*/
|
||
#ifdef SQLITE_TEST
|
||
+# if SQLITE_OS_WIN
|
||
+SQLITE_API extern int sqlite3_current_time; /* Fake system time in seconds since 1970. */
|
||
+# else
|
||
SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
|
||
+# endif
|
||
#endif
|
||
|
||
/*
|
||
@@ -32589,7 +32623,15 @@ static int proxyClose(sqlite3_file *id)
|
||
** necessarily been initialized when this routine is called, and so they
|
||
** should not be used.
|
||
*/
|
||
-SQLITE_API int sqlite3_os_init(void){
|
||
+#if SQLITE_OS_WIN
|
||
+#if defined(SQLITE_AMALGAMATION)
|
||
+static
|
||
+#endif
|
||
+SQLITE_API int sqlite3_os_unix_init(void){
|
||
+#else
|
||
+SQLITE_API int sqlite3_os_init(void){
|
||
+#endif
|
||
+
|
||
/*
|
||
** The following macro defines an initializer for an sqlite3_vfs object.
|
||
** The name of the VFS is NAME. The pAppData is a pointer to a pointer
|
||
@@ -32645,20 +32687,26 @@ SQLITE_API int sqlite3_os_init(void){
|
||
static sqlite3_vfs aVfs[] = {
|
||
#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__))
|
||
UNIXVFS("unix", autolockIoFinder ),
|
||
+#elif defined(__CYGWIN__) && defined(HAVE_FLOCK)
|
||
+ UNIXVFS("unix", cygwinIoFinder ),
|
||
#else
|
||
UNIXVFS("unix", posixIoFinder ),
|
||
#endif
|
||
UNIXVFS("unix-none", nolockIoFinder ),
|
||
UNIXVFS("unix-dotfile", dotlockIoFinder ),
|
||
+#if defined(__CYGWIN__) && defined(HAVE_FLOCK)
|
||
+ UNIXVFS("unix-excl", cygwinIoFinder ),
|
||
+#else
|
||
UNIXVFS("unix-excl", posixIoFinder ),
|
||
+#endif
|
||
#if OS_VXWORKS
|
||
UNIXVFS("unix-namedsem", semIoFinder ),
|
||
#endif
|
||
-#if SQLITE_ENABLE_LOCKING_STYLE
|
||
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__CYGWIN__)
|
||
UNIXVFS("unix-posix", posixIoFinder ),
|
||
-#if !OS_VXWORKS
|
||
- UNIXVFS("unix-flock", flockIoFinder ),
|
||
#endif
|
||
+#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS || defined(HAVE_FLOCK)
|
||
+ UNIXVFS("unix-flock", flockIoFinder ),
|
||
#endif
|
||
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
||
UNIXVFS("unix-afp", afpIoFinder ),
|
||
@@ -32679,6 +32727,8 @@ SQLITE_API int sqlite3_os_init(void){
|
||
return SQLITE_OK;
|
||
}
|
||
|
||
+#undef aSyscall
|
||
+#if !SQLITE_OS_WIN
|
||
/*
|
||
** Shutdown the operating system interface.
|
||
**
|
||
@@ -32689,6 +32739,7 @@ SQLITE_API int sqlite3_os_init(void){
|
||
SQLITE_API int sqlite3_os_end(void){
|
||
return SQLITE_OK;
|
||
}
|
||
+#endif /* !SQLITE_OS_WIN */
|
||
|
||
#endif /* SQLITE_OS_UNIX */
|
||
|
||
@@ -32945,7 +32996,7 @@ SQLITE_API int sqlite3_open_file_count =
|
||
** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
|
||
** based on the sub-platform)?
|
||
*/
|
||
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
|
||
+#if 0
|
||
# define SQLITE_WIN32_HAS_ANSI
|
||
#endif
|
||
|
||
@@ -33319,8 +33370,6 @@ SQLITE_PRIVATE const sqlite3_mem_methods
|
||
*/
|
||
#ifdef SQLITE_TEST
|
||
SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
||
-#else
|
||
-static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
|
||
#endif
|
||
|
||
#ifndef SYSCALL
|
||
@@ -33414,7 +33463,7 @@ static struct win_syscall {
|
||
#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
|
||
DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
|
||
|
||
-#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
|
||
+#if SQLITE_OS_WINCE
|
||
{ "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
|
||
#else
|
||
{ "CreateMutexW", (SYSCALL)0, 0 },
|
||
@@ -33445,7 +33494,7 @@ static struct win_syscall {
|
||
{ "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
|
||
#endif
|
||
|
||
-#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
|
||
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \
|
||
LPFILETIME))aSyscall[11].pCurrent)
|
||
|
||
#if SQLITE_OS_WINCE
|
||
@@ -33454,7 +33503,7 @@ static struct win_syscall {
|
||
{ "FileTimeToSystemTime", (SYSCALL)0, 0 },
|
||
#endif
|
||
|
||
-#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
|
||
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \
|
||
LPSYSTEMTIME))aSyscall[12].pCurrent)
|
||
|
||
{ "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
|
||
@@ -33564,7 +33613,7 @@ static struct win_syscall {
|
||
|
||
#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
|
||
|
||
-#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) || defined(_WIN32)
|
||
#if SQLITE_OS_WINCE
|
||
/* The GetProcAddressA() routine is only available on Windows CE. */
|
||
{ "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
|
||
@@ -33723,7 +33772,7 @@ static struct win_syscall {
|
||
|
||
#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
|
||
|
||
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
||
+#ifdef SQLITE_WIN32_HAS_ANSI
|
||
{ "LockFile", (SYSCALL)LockFile, 0 },
|
||
#else
|
||
{ "LockFile", (SYSCALL)0, 0 },
|
||
@@ -33791,12 +33840,16 @@ static struct win_syscall {
|
||
|
||
#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
|
||
|
||
+#if SQLITE_OS_WINCE
|
||
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
|
||
+#else
|
||
+ { "SystemTimeToFileTime", (SYSCALL)0, 0 },
|
||
+#endif
|
||
|
||
-#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
|
||
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \
|
||
LPFILETIME))aSyscall[56].pCurrent)
|
||
|
||
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
||
+#ifdef SQLITE_WIN32_HAS_ANSI
|
||
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
|
||
#else
|
||
{ "UnlockFile", (SYSCALL)0, 0 },
|
||
@@ -33843,7 +33896,7 @@ static struct win_syscall {
|
||
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
|
||
DWORD,DWORD))aSyscall[62].pCurrent)
|
||
|
||
-#if !SQLITE_OS_WINRT
|
||
+#if SQLITE_OS_WINCE
|
||
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
|
||
#else
|
||
{ "WaitForSingleObject", (SYSCALL)0, 0 },
|
||
@@ -33957,6 +34010,7 @@ static struct win_syscall {
|
||
** is really just a macro that uses a compiler intrinsic (e.g. x64).
|
||
** So do not try to make this is into a redefinable interface.
|
||
*/
|
||
+#if 0
|
||
#if defined(InterlockedCompareExchange)
|
||
{ "InterlockedCompareExchange", (SYSCALL)0, 0 },
|
||
|
||
@@ -33967,6 +34021,61 @@ static struct win_syscall {
|
||
#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
|
||
SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
|
||
#endif /* defined(InterlockedCompareExchange) */
|
||
+#endif /* 0*/
|
||
+
|
||
+#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
|
||
+ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 },
|
||
+#else
|
||
+ { "GetModuleHandleW", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[76].pCurrent)
|
||
+
|
||
+#if defined(SQLITE_WIN32_HAS_WIDE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||
+ { "SetDllDirectoryW", (SYSCALL)SetDllDirectoryW, 0 },
|
||
+#else
|
||
+ { "SetDllDirectoryW", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define osSetDllDirectoryW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[77].pCurrent)
|
||
+
|
||
+#if defined(__CYGWIN__)
|
||
+ { "getenv", (SYSCALL)getenv, 0 },
|
||
+#else
|
||
+ { "getenv", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define getenv ((const char *(*)(const char *))aSyscall[78].pCurrent)
|
||
+
|
||
+#if defined(__CYGWIN__)
|
||
+ { "getcwd", (SYSCALL)getcwd, 0 },
|
||
+#else
|
||
+ { "getcwd", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define getcwd ((char*(*)(char*,size_t))aSyscall[79].pCurrent)
|
||
+
|
||
+#if defined(__CYGWIN__)
|
||
+ { "__errno", (SYSCALL)__errno, 0 },
|
||
+#else
|
||
+ { "__errno", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define osErrno (*((int*(*)(void))aSyscall[80].pCurrent)())
|
||
+
|
||
+#if defined(__CYGWIN__) && defined(SQLITE_WIN32_HAS_WIDE)
|
||
+ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 },
|
||
+#else
|
||
+ { "cygwin_conv_path", (SYSCALL)0, 0 },
|
||
+#endif
|
||
+
|
||
+#define cygwin_conv_path ((size_t(*)(unsigned int, \
|
||
+ const void *, void *, size_t))aSyscall[81].pCurrent)
|
||
+
|
||
+ { "cygwin_conv_to_full_win32_path", (SYSCALL)0, 0 },
|
||
+
|
||
+#define cygwin_conv_to_full_win32_path ((void(*)(const char *, \
|
||
+ char *))aSyscall[82].pCurrent)
|
||
|
||
}; /* End of the overrideable system calls */
|
||
|
||
@@ -34141,6 +34250,7 @@ SQLITE_API int sqlite3_win32_reset_heap(
|
||
}
|
||
#endif /* SQLITE_WIN32_MALLOC */
|
||
|
||
+#ifdef _WIN32
|
||
/*
|
||
** This function outputs the specified (ANSI) string to the Win32 debugger
|
||
** (if available).
|
||
@@ -34177,6 +34287,7 @@ SQLITE_API void sqlite3_win32_write_debu
|
||
}
|
||
#endif
|
||
}
|
||
+#endif /* _WIN32 */
|
||
|
||
/*
|
||
** The following routine suspends the current thread for at least ms
|
||
@@ -34186,6 +34297,9 @@ SQLITE_API void sqlite3_win32_write_debu
|
||
static HANDLE sleepObj = NULL;
|
||
#endif
|
||
|
||
+#if defined(__CYGWIN__) && defined(SQLITE_AMALGAMATION)
|
||
+static
|
||
+#endif
|
||
SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
|
||
#if SQLITE_OS_WINRT
|
||
if ( sleepObj==NULL ){
|
||
@@ -34200,7 +34314,7 @@ SQLITE_API void sqlite3_win32_sleep(DWOR
|
||
}
|
||
|
||
#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
||
- SQLITE_THREADSAFE>0
|
||
+ SQLITE_THREADSAFE>0 && (defined(_MSC_VER) || defined(__MSVCRT__))
|
||
SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
|
||
DWORD rc;
|
||
while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
|
||
@@ -34231,6 +34345,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HA
|
||
# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
|
||
#endif
|
||
|
||
+#if 0
|
||
/*
|
||
** This function determines if the machine is running a version of Windows
|
||
** based on the NT kernel.
|
||
@@ -34269,6 +34384,7 @@ SQLITE_API int sqlite3_win32_is_nt(void)
|
||
return 1;
|
||
#endif
|
||
}
|
||
+#endif /* 0 */
|
||
|
||
#ifdef SQLITE_WIN32_MALLOC
|
||
/*
|
||
@@ -34475,12 +34591,13 @@ SQLITE_PRIVATE void sqlite3MemSetDefault
|
||
}
|
||
#endif /* SQLITE_WIN32_MALLOC */
|
||
|
||
+#ifdef _WIN32
|
||
/*
|
||
** Convert a UTF-8 string to Microsoft Unicode (UTF-16?).
|
||
**
|
||
** Space to hold the returned string is obtained from malloc.
|
||
*/
|
||
-static LPWSTR winUtf8ToUnicode(const char *zFilename){
|
||
+static LPWSTR winUtf8ToUnicode(const char *zFilename, WCHAR *buf){
|
||
int nChar;
|
||
LPWSTR zWideFilename;
|
||
|
||
@@ -34488,9 +34605,13 @@ static LPWSTR winUtf8ToUnicode(const cha
|
||
if( nChar==0 ){
|
||
return 0;
|
||
}
|
||
- zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) );
|
||
- if( zWideFilename==0 ){
|
||
- return 0;
|
||
+ if( buf && ((size_t)nChar<=MAX_PATH) ){
|
||
+ zWideFilename = buf;
|
||
+ }else{
|
||
+ zWideFilename = sqlite3Malloc( nChar*sizeof(WCHAR) );
|
||
+ if( zWideFilename==0 ){
|
||
+ return 0;
|
||
+ }
|
||
}
|
||
nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
|
||
nChar);
|
||
@@ -34500,6 +34621,7 @@ static LPWSTR winUtf8ToUnicode(const cha
|
||
}
|
||
return zWideFilename;
|
||
}
|
||
+#endif /* _WIN32 */
|
||
|
||
/*
|
||
** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
|
||
@@ -34539,7 +34661,7 @@ static LPWSTR winMbcsToUnicode(const cha
|
||
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||
|
||
nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
|
||
- 0)*sizeof(WCHAR);
|
||
+ 0);
|
||
if( nByte==0 ){
|
||
return 0;
|
||
}
|
||
@@ -34556,6 +34678,7 @@ static LPWSTR winMbcsToUnicode(const cha
|
||
return zMbcsFilename;
|
||
}
|
||
|
||
+#ifdef _WIN32
|
||
/*
|
||
** Convert Microsoft Unicode to multi-byte character string, based on the
|
||
** user's ANSI codepage.
|
||
@@ -34584,6 +34707,7 @@ static char *winUnicodeToMbcs(LPCWSTR zW
|
||
}
|
||
return zFilename;
|
||
}
|
||
+#endif /* _WIN32 */
|
||
|
||
/*
|
||
** Convert multibyte character string to UTF-8. Space to hold the
|
||
@@ -34602,6 +34726,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_u
|
||
return zFilenameUtf8;
|
||
}
|
||
|
||
+#ifdef _WIN32
|
||
/*
|
||
** Convert UTF-8 to multibyte character string. Space to hold the
|
||
** returned string is obtained from sqlite3_malloc().
|
||
@@ -34609,13 +34734,16 @@ SQLITE_API char *sqlite3_win32_mbcs_to_u
|
||
SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
|
||
char *zFilenameMbcs;
|
||
LPWSTR zTmpWide;
|
||
+ WCHAR buf[MAX_PATH];
|
||
|
||
- zTmpWide = winUtf8ToUnicode(zFilename);
|
||
+ zTmpWide = winUtf8ToUnicode(zFilename, buf);
|
||
if( zTmpWide==0 ){
|
||
return 0;
|
||
}
|
||
zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
|
||
- sqlite3_free(zTmpWide);
|
||
+ if( zTmpWide!=buf ){
|
||
+ sqlite3_free(zTmpWide);
|
||
+ }
|
||
return zFilenameMbcs;
|
||
}
|
||
|
||
@@ -34641,7 +34769,7 @@ SQLITE_API int sqlite3_win32_set_directo
|
||
|| type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
|
||
);
|
||
assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
|
||
- if( ppDirectory ){
|
||
+ if( !getenv && ppDirectory ){
|
||
char *zValueUtf8 = 0;
|
||
if( zValue && zValue[0] ){
|
||
zValueUtf8 = winUnicodeToUtf8(zValue);
|
||
@@ -34655,6 +34783,7 @@ SQLITE_API int sqlite3_win32_set_directo
|
||
}
|
||
return SQLITE_ERROR;
|
||
}
|
||
+#endif /* _WIN32 */
|
||
|
||
/*
|
||
** The return value of winGetLastErrorMsg
|
||
@@ -34919,8 +35048,9 @@ static int winceCreateLock(const char *z
|
||
DWORD lastErrno;
|
||
BOOL bLogged = FALSE;
|
||
BOOL bInit = TRUE;
|
||
+ WCHAR buf[MAX_PATH];
|
||
|
||
- zName = winUtf8ToUnicode(zFilename);
|
||
+ zName = winUtf8ToUnicode(zFilename, buf);
|
||
if( zName==0 ){
|
||
/* out of memory */
|
||
return SQLITE_IOERR_NOMEM;
|
||
@@ -34940,7 +35070,9 @@ static int winceCreateLock(const char *z
|
||
pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
|
||
if (!pFile->hMutex){
|
||
pFile->lastErrno = osGetLastError();
|
||
- sqlite3_free(zName);
|
||
+ if( zName!=buf ){
|
||
+ sqlite3_free(zName);
|
||
+ }
|
||
return winLogError(SQLITE_IOERR, pFile->lastErrno,
|
||
"winceCreateLock1", zFilename);
|
||
}
|
||
@@ -34964,7 +35096,9 @@ static int winceCreateLock(const char *z
|
||
bInit = FALSE;
|
||
}
|
||
|
||
- sqlite3_free(zName);
|
||
+ if( zName!=buf ){
|
||
+ sqlite3_free(zName);
|
||
+ }
|
||
|
||
/* If we succeeded in making the shared memory handle, map it. */
|
||
if( pFile->hShared ){
|
||
@@ -35198,9 +35332,11 @@ static BOOL winLockFile(
|
||
ovlp.Offset = offsetLow;
|
||
ovlp.OffsetHigh = offsetHigh;
|
||
return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
|
||
+#ifdef SQLITE_WIN32_HAS_ANSI
|
||
}else{
|
||
return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
||
numBytesHigh);
|
||
+#endif
|
||
}
|
||
#endif
|
||
}
|
||
@@ -35229,9 +35365,11 @@ static BOOL winUnlockFile(
|
||
ovlp.Offset = offsetLow;
|
||
ovlp.OffsetHigh = offsetHigh;
|
||
return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
|
||
+#ifdef SQLITE_WIN32_HAS_ANSI
|
||
}else{
|
||
return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
||
numBytesHigh);
|
||
+#endif
|
||
}
|
||
#endif
|
||
}
|
||
@@ -35702,12 +35840,13 @@ static int winFileSize(sqlite3_file *id,
|
||
DWORD lastErrno;
|
||
|
||
lowerBits = osGetFileSize(pFile->h, &upperBits);
|
||
- *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
|
||
if( (lowerBits == INVALID_FILE_SIZE)
|
||
&& ((lastErrno = osGetLastError())!=NO_ERROR) ){
|
||
pFile->lastErrno = lastErrno;
|
||
rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
|
||
"winFileSize", pFile->zPath);
|
||
+ }else{
|
||
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
|
||
}
|
||
}
|
||
#endif
|
||
@@ -37064,6 +37203,7 @@ static int winUnfetch(sqlite3_file *fd,
|
||
assert( pFd->nFetchOut>=0 );
|
||
#endif
|
||
|
||
+ UNUSED_PARAMETER(iOff);
|
||
OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
||
osGetCurrentProcessId(), fd));
|
||
return SQLITE_OK;
|
||
@@ -37108,7 +37248,7 @@ static const sqlite3_io_methods winIoMet
|
||
** sqlite3_vfs object.
|
||
*/
|
||
|
||
-#if defined(__CYGWIN__)
|
||
+#if 0
|
||
/*
|
||
** Convert a filename from whatever the underlying operating system
|
||
** supports for filenames into UTF-8. Space to hold the result is
|
||
@@ -37133,12 +37273,106 @@ static char *winConvertToUtf8Filename(co
|
||
** Convert a UTF-8 filename into whatever form the underlying
|
||
** operating system wants filenames in. Space to hold the result
|
||
** is obtained from malloc and must be freed by the calling
|
||
-** function.
|
||
+** function, unless buf is not NULL and the needed size is less
|
||
+** than MAX_PATH bytes.
|
||
+**
|
||
+** On Cygwin 1.7 and higher, 3 possible input forms are accepted:
|
||
+** - If the filename starts with "<drive>:/" or "<drive>:\",
|
||
+** it is converted to UTF-16 as-is.
|
||
+** - If the filename contains '/', it is assumed to be a
|
||
+** Cygwin absolute path, it is converted to a win32
|
||
+** absolute path in UTF-16.
|
||
+** - Otherwise it must be a filename only, the win32 filename
|
||
+** is returned in UTF-16.
|
||
+** Note: The function cygwin_conv_path does not exist in
|
||
+** Cygwin 1.5. Cygwin 1.7 does not run in Windows 95/98/ME.
|
||
+** Therefore the !osIsNT() case does not need special handling.
|
||
+** Note 2: If the function cygwin_conv_path() fails, only
|
||
+** UTF-8 -> UTF-16 conversion will be done. This can only
|
||
+** happen when the file path >32k, in which case winUtf8ToUnicode()
|
||
+** will fail too.
|
||
*/
|
||
-static void *winConvertFromUtf8Filename(const char *zFilename){
|
||
+static void *winConvertFromUtf8Filename(const char *zFilename, WCHAR *buf){
|
||
void *zConverted = 0;
|
||
if( osIsNT() ){
|
||
- zConverted = winUtf8ToUnicode(zFilename);
|
||
+ int nChar;
|
||
+ LPWSTR zWideFilename;
|
||
+
|
||
+ if( cygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
|
||
+ && winIsDirSep(zFilename[2])) ){
|
||
+ int nByte;
|
||
+ int convertflag = CCP_POSIX_TO_WIN_W;
|
||
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
|
||
+ nByte = (int) cygwin_conv_path(convertflag,
|
||
+ zFilename, 0, 0);
|
||
+ if( nByte>0 ){
|
||
+ if( buf && (nByte<(MAX_PATH-12)) ){
|
||
+ zConverted = buf;
|
||
+ }else{
|
||
+ zConverted = sqlite3Malloc(nByte+12);
|
||
+ if ( zConverted==0 ){
|
||
+ return zConverted;
|
||
+ }
|
||
+ }
|
||
+ zWideFilename = zConverted;
|
||
+ /* Filenames should be prefixed, except when converted
|
||
+ * full path already starts with "\\?\". */
|
||
+ if( cygwin_conv_path(convertflag, zFilename,
|
||
+ zWideFilename+4, nByte)==0 ){
|
||
+ if( (convertflag&CCP_RELATIVE) ){
|
||
+ memmove(zWideFilename, zWideFilename+4, nByte);
|
||
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
|
||
+ memcpy(zWideFilename, L"\\\\?\\", 8);
|
||
+ }else if( zWideFilename[6]!='?' ){
|
||
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
|
||
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
|
||
+ }else{
|
||
+ memmove(zWideFilename, zWideFilename+4, nByte);
|
||
+ }
|
||
+ return zConverted;
|
||
+ }
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
+ }
|
||
+#ifdef _WIN32
|
||
+ }else if( (cygwin_conv_to_full_win32_path!=NULL) &&
|
||
+ !(winIsDriveLetterAndColon(zFilename) && winIsDirSep(zFilename[2]))){
|
||
+ char buf1[MAX_PATH];
|
||
+ cygwin_conv_to_full_win32_path(zFilename, buf1);
|
||
+ return winMbcsToUnicode(buf1);
|
||
+#endif
|
||
+ }
|
||
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
|
||
+ if( nChar==0 ){
|
||
+ return 0;
|
||
+ }
|
||
+ if( buf && ((size_t)nChar<=MAX_PATH) ){
|
||
+ zWideFilename = buf;
|
||
+ }else{
|
||
+ zWideFilename = sqlite3Malloc( nChar*sizeof(WCHAR)+12 );
|
||
+ if( zWideFilename==0 ){
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
|
||
+ zWideFilename, nChar);
|
||
+ if( nChar==0 ){
|
||
+ sqlite3_free(zWideFilename);
|
||
+ zWideFilename = 0;
|
||
+ }else if( nChar>MAX_PATH
|
||
+ && winIsDriveLetterAndColon(zFilename)
|
||
+ && winIsDirSep(zFilename[2]) ){
|
||
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
|
||
+ zWideFilename[2] = '\\';
|
||
+ memcpy(zWideFilename, L"\\\\?\\", 8);
|
||
+ }else if( nChar>MAX_PATH
|
||
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
|
||
+ && zFilename[2] != '?' ){
|
||
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
|
||
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
|
||
+ }
|
||
+ zConverted = zWideFilename;
|
||
}
|
||
#ifdef SQLITE_WIN32_HAS_ANSI
|
||
else{
|
||
@@ -37161,7 +37395,14 @@ static int winMakeEndInDirSep(int nBuf,
|
||
if( winIsDirSep(zBuf[nLen-1]) ){
|
||
return 1;
|
||
}else if( nLen+1<nBuf ){
|
||
- zBuf[nLen] = winGetDirSep();
|
||
+ if( !getenv ){
|
||
+ zBuf[nLen] = '\\';
|
||
+ }else if( winIsDriveLetterAndColon(zBuf) && winIsDirSep(zBuf[2]) ){
|
||
+ zBuf[nLen] = '\\';
|
||
+ zBuf[2]='\\';
|
||
+ }else{
|
||
+ zBuf[nLen] = '/';
|
||
+ }
|
||
zBuf[nLen+1] = '\0';
|
||
return 1;
|
||
}
|
||
@@ -37175,7 +37416,7 @@ static int winMakeEndInDirSep(int nBuf,
|
||
** The pointer returned in pzBuf must be freed via sqlite3_free().
|
||
*/
|
||
static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
|
||
- static char zChars[] =
|
||
+ static const char zChars[] =
|
||
"abcdefghijklmnopqrstuvwxyz"
|
||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||
"0123456789";
|
||
@@ -37220,8 +37461,8 @@ static int winGetTempname(sqlite3_vfs *p
|
||
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
|
||
}
|
||
}
|
||
-#if defined(__CYGWIN__)
|
||
- else{
|
||
+#if SQLITE_OS_WINNT
|
||
+ else if( getenv!=NULL ){
|
||
static const char *azDirs[] = {
|
||
0, /* getenv("SQLITE_TMPDIR") */
|
||
0, /* getenv("TMPDIR") */
|
||
@@ -37244,14 +37485,15 @@ static int winGetTempname(sqlite3_vfs *p
|
||
if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
|
||
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
|
||
void *zConverted;
|
||
+ WCHAR buf[MAX_PATH];
|
||
if( zDir==0 ) continue;
|
||
/* If the path starts with a drive letter followed by the colon
|
||
** character, assume it is already a native Win32 path; otherwise,
|
||
** it must be converted to a native Win32 path via the Cygwin API
|
||
** prior to using it.
|
||
*/
|
||
- if( winIsDriveLetterAndColon(zDir) ){
|
||
- zConverted = winConvertFromUtf8Filename(zDir);
|
||
+ {
|
||
+ zConverted = winConvertFromUtf8Filename(zDir, buf);
|
||
if( !zConverted ){
|
||
sqlite3_free(zBuf);
|
||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||
@@ -37259,10 +37501,15 @@ static int winGetTempname(sqlite3_vfs *p
|
||
}
|
||
if( winIsDir(zConverted) ){
|
||
sqlite3_snprintf(nMax, zBuf, "%s", zDir);
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
break;
|
||
}
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
+#if 0 /* No longer necessary */
|
||
}else{
|
||
zConverted = sqlite3MallocZero( nMax+1 );
|
||
if( !zConverted ){
|
||
@@ -37270,7 +37517,7 @@ static int winGetTempname(sqlite3_vfs *p
|
||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||
return SQLITE_IOERR_NOMEM;
|
||
}
|
||
- if( cygwin_conv_path(
|
||
+ if( (int) cygwin_conv_path(
|
||
osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
|
||
zConverted, nMax+1)<0 ){
|
||
sqlite3_free(zConverted);
|
||
@@ -37297,13 +37544,16 @@ static int winGetTempname(sqlite3_vfs *p
|
||
break;
|
||
}
|
||
sqlite3_free(zConverted);
|
||
+#endif /* No longer necessary */
|
||
}
|
||
}
|
||
}
|
||
-#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
||
+#endif
|
||
+
|
||
+#if !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
||
else if( osIsNT() ){
|
||
char *zMulti;
|
||
- LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
|
||
+ LPWSTR zWidePath = sqlite3Malloc( nMax*sizeof(WCHAR) );
|
||
if( !zWidePath ){
|
||
sqlite3_free(zBuf);
|
||
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
||
@@ -37445,9 +37695,7 @@ static int winOpen(
|
||
DWORD dwShareMode;
|
||
DWORD dwCreationDisposition;
|
||
DWORD dwFlagsAndAttributes = 0;
|
||
-#if SQLITE_OS_WINCE
|
||
- int isTemp = 0;
|
||
-#endif
|
||
+ WCHAR buf[MAX_PATH];
|
||
winFile *pFile = (winFile*)id;
|
||
void *zConverted; /* Filename in OS encoding */
|
||
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
|
||
@@ -37538,7 +37786,11 @@ static int winOpen(
|
||
zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
|
||
|
||
/* Convert the filename to the system encoding. */
|
||
- zConverted = winConvertFromUtf8Filename(zUtf8Name);
|
||
+#if SQLITE_OS_WINCE
|
||
+ zConverted = winConvertFromUtf8Filename(zUtf8Name, isDelete ? 0 : buf );
|
||
+#else
|
||
+ zConverted = winConvertFromUtf8Filename(zUtf8Name, buf );
|
||
+#endif
|
||
if( zConverted==0 ){
|
||
sqlite3_free(zTmpname);
|
||
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
|
||
@@ -37546,7 +37798,9 @@ static int winOpen(
|
||
}
|
||
|
||
if( winIsDir(zConverted) ){
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
sqlite3_free(zTmpname);
|
||
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
|
||
return SQLITE_CANTOPEN_ISDIR;
|
||
@@ -37579,7 +37833,6 @@ static int winOpen(
|
||
if( isDelete ){
|
||
#if SQLITE_OS_WINCE
|
||
dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
|
||
- isTemp = 1;
|
||
#else
|
||
dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
|
||
| FILE_ATTRIBUTE_HIDDEN
|
||
@@ -37645,7 +37898,9 @@ static int winOpen(
|
||
if( h==INVALID_HANDLE_VALUE ){
|
||
pFile->lastErrno = lastErrno;
|
||
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
sqlite3_free(zTmpname);
|
||
if( isReadWrite && !isExclusive ){
|
||
return winOpen(pVfs, zName, id,
|
||
@@ -37674,17 +37929,21 @@ static int winOpen(
|
||
&& (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
|
||
){
|
||
osCloseHandle(h);
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
sqlite3_free(zTmpname);
|
||
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
|
||
return rc;
|
||
}
|
||
- if( isTemp ){
|
||
+ if( isDelete ){
|
||
pFile->zDeleteOnClose = zConverted;
|
||
}else
|
||
#endif
|
||
{
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
}
|
||
|
||
sqlite3_free(zTmpname);
|
||
@@ -37733,13 +37992,14 @@ static int winDelete(
|
||
DWORD attr;
|
||
DWORD lastErrno = 0;
|
||
void *zConverted;
|
||
+ WCHAR buf[MAX_PATH];
|
||
UNUSED_PARAMETER(pVfs);
|
||
UNUSED_PARAMETER(syncDir);
|
||
|
||
SimulateIOError(return SQLITE_IOERR_DELETE);
|
||
OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
|
||
|
||
- zConverted = winConvertFromUtf8Filename(zFilename);
|
||
+ zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
||
if( zConverted==0 ){
|
||
OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
||
return SQLITE_IOERR_NOMEM;
|
||
@@ -37823,7 +38083,9 @@ static int winDelete(
|
||
}else{
|
||
winLogIoerr(cnt);
|
||
}
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted != buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
|
||
return rc;
|
||
}
|
||
@@ -37841,13 +38103,14 @@ static int winAccess(
|
||
int rc = 0;
|
||
DWORD lastErrno = 0;
|
||
void *zConverted;
|
||
+ WCHAR buf[MAX_PATH];
|
||
UNUSED_PARAMETER(pVfs);
|
||
|
||
SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
||
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
|
||
zFilename, flags, pResOut));
|
||
|
||
- zConverted = winConvertFromUtf8Filename(zFilename);
|
||
+ zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
||
if( zConverted==0 ){
|
||
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
|
||
return SQLITE_IOERR_NOMEM;
|
||
@@ -37873,7 +38136,9 @@ static int winAccess(
|
||
}else{
|
||
winLogIoerr(cnt);
|
||
if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
|
||
zFilename);
|
||
}else{
|
||
@@ -37886,7 +38151,9 @@ static int winAccess(
|
||
attr = osGetFileAttributesA((char*)zConverted);
|
||
}
|
||
#endif
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
switch( flags ){
|
||
case SQLITE_ACCESS_READ:
|
||
case SQLITE_ACCESS_EXISTS:
|
||
@@ -37915,6 +38182,7 @@ static BOOL winIsDriveLetterAndColon(
|
||
return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
|
||
}
|
||
|
||
+#ifdef _WIN32
|
||
/*
|
||
** Returns non-zero if the specified path name should be used verbatim. If
|
||
** non-zero is returned from this function, the calling function must simply
|
||
@@ -37951,6 +38219,42 @@ static BOOL winIsVerbatimPathname(
|
||
*/
|
||
return FALSE;
|
||
}
|
||
+#endif /* _WIN32 */
|
||
+
|
||
+/*
|
||
+** Simplify a filename into its canonical form
|
||
+** by making the following changes:
|
||
+**
|
||
+** * convert any '/' to '\' (win32) or reverse (Cygwin)
|
||
+** * removing any trailing and duplicate / (except for UNC paths)
|
||
+** * convert /./ into just /
|
||
+**
|
||
+** Changes are made in-place. Return the new name length.
|
||
+**
|
||
+** The original filename is in z[0..]. If the path is shortened,
|
||
+** no-longer used bytes will be written by '\0'.
|
||
+*/
|
||
+static void winSimplifyName(char *z){
|
||
+ int i, j;
|
||
+ for(i=j=0; z[i]; ++i){
|
||
+ if( winIsDirSep(z[i]) ){
|
||
+#if !defined(SQLITE_TEST)
|
||
+ /* Some test-cases assume that "./foo" and "foo" are different */
|
||
+ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){
|
||
+ ++i;
|
||
+ continue;
|
||
+ }
|
||
+#endif
|
||
+ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){
|
||
+ continue;
|
||
+ }
|
||
+ z[j++] = getenv?'/':'\\';
|
||
+ }else{
|
||
+ z[j++] = z[i];
|
||
+ }
|
||
+ }
|
||
+ while(j<i) z[j++] = '\0';
|
||
+}
|
||
|
||
/*
|
||
** Turn a relative pathname into a full pathname. Write the full
|
||
@@ -37964,7 +38268,33 @@ static int winFullPathname(
|
||
char *zFull /* Output buffer */
|
||
){
|
||
|
||
-#if defined(__CYGWIN__)
|
||
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
||
+ DWORD nByte;
|
||
+ void *zConverted;
|
||
+ char *zOut;
|
||
+ WCHAR buf[MAX_PATH];
|
||
+#endif
|
||
+
|
||
+ SimulateIOError( return SQLITE_ERROR );
|
||
+
|
||
+ if( getcwd ){
|
||
+ zFull[nFull-1] = '\0';
|
||
+ if( winIsDirSep(zRelative[0]) ){
|
||
+ sqlite3_snprintf(nFull, zFull, "%s", zRelative);
|
||
+ winSimplifyName(zFull);
|
||
+ return SQLITE_OK;
|
||
+ }else if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){
|
||
+ int nCwd;
|
||
+ if( getcwd(zFull, nFull-1)==0 ){
|
||
+ return winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "getcwd", zRelative);
|
||
+ }
|
||
+ nCwd = (int)strlen(zFull);
|
||
+ sqlite3_snprintf(nFull-nCwd, &zFull[nCwd], "/%s", zRelative);
|
||
+ winSimplifyName(zFull);
|
||
+ return SQLITE_OK;
|
||
+ }
|
||
+ }
|
||
+#if 0
|
||
SimulateIOError( return SQLITE_ERROR );
|
||
UNUSED_PARAMETER(nFull);
|
||
assert( nFull>=pVfs->mxPathname );
|
||
@@ -37979,7 +38309,7 @@ static int winFullPathname(
|
||
if( !zOut ){
|
||
return SQLITE_IOERR_NOMEM;
|
||
}
|
||
- if( cygwin_conv_path(
|
||
+ if( (int) cygwin_conv_path(
|
||
(osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
|
||
CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
|
||
sqlite3_free(zOut);
|
||
@@ -38001,7 +38331,7 @@ static int winFullPathname(
|
||
if( !zOut ){
|
||
return SQLITE_IOERR_NOMEM;
|
||
}
|
||
- if( cygwin_conv_path(
|
||
+ if( (int) cygwin_conv_path(
|
||
(osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
|
||
zRelative, zOut, pVfs->mxPathname+1)<0 ){
|
||
sqlite3_free(zOut);
|
||
@@ -38022,7 +38352,6 @@ static int winFullPathname(
|
||
#endif
|
||
|
||
#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
|
||
- SimulateIOError( return SQLITE_ERROR );
|
||
/* WinCE has no concept of a relative pathname, or so I am told. */
|
||
/* WinRT has no way to convert a relative path to an absolute one. */
|
||
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
||
@@ -38040,10 +38369,12 @@ static int winFullPathname(
|
||
return SQLITE_OK;
|
||
#endif
|
||
|
||
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
||
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
|
||
+#if 0
|
||
DWORD nByte;
|
||
void *zConverted;
|
||
char *zOut;
|
||
+#endif
|
||
|
||
/* If this path name begins with "/X:", where "X" is any alphabetic
|
||
** character, discard the initial "/" from the pathname.
|
||
@@ -38052,12 +38383,7 @@ static int winFullPathname(
|
||
zRelative++;
|
||
}
|
||
|
||
- /* It's odd to simulate an io-error here, but really this is just
|
||
- ** using the io-error infrastructure to test that SQLite handles this
|
||
- ** function failing. This function could fail if, for example, the
|
||
- ** current working directory has been unlinked.
|
||
- */
|
||
- SimulateIOError( return SQLITE_ERROR );
|
||
+#if defined(_WIN32)
|
||
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
|
||
/*
|
||
** NOTE: We are dealing with a relative path name and the data
|
||
@@ -38069,7 +38395,8 @@ static int winFullPathname(
|
||
sqlite3_data_directory, winGetDirSep(), zRelative);
|
||
return SQLITE_OK;
|
||
}
|
||
- zConverted = winConvertFromUtf8Filename(zRelative);
|
||
+#endif
|
||
+ zConverted = winConvertFromUtf8Filename(zRelative, buf);
|
||
if( zConverted==0 ){
|
||
return SQLITE_IOERR_NOMEM;
|
||
}
|
||
@@ -38077,24 +38404,32 @@ static int winFullPathname(
|
||
LPWSTR zTemp;
|
||
nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
|
||
if( nByte==0 ){
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted != buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
||
"winFullPathname1", zRelative);
|
||
}
|
||
nByte += 3;
|
||
- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
|
||
+ zTemp = sqlite3Malloc( nByte*sizeof(zTemp[0]) );
|
||
if( zTemp==0 ){
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted != buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
return SQLITE_IOERR_NOMEM;
|
||
}
|
||
nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
|
||
if( nByte==0 ){
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted != buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
sqlite3_free(zTemp);
|
||
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
|
||
"winFullPathname2", zRelative);
|
||
}
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted != buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
zOut = winUnicodeToUtf8(zTemp);
|
||
sqlite3_free(zTemp);
|
||
}
|
||
@@ -38126,7 +38461,22 @@ static int winFullPathname(
|
||
}
|
||
#endif
|
||
if( zOut ){
|
||
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
|
||
+ if( memcmp(zOut, "\\\\?\\", 4) ){
|
||
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
|
||
+ }else if( memcmp(zOut+4, "UNC\\", 4) ){
|
||
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4);
|
||
+ }else{
|
||
+ char *p = zOut+6;
|
||
+ *p = '\\';
|
||
+ if( getcwd ){
|
||
+ /* On Cygwin, UNC paths use forward slashes */
|
||
+ while( *p ){
|
||
+ if( *p=='\\' ) *p = '/';
|
||
+ ++p;
|
||
+ }
|
||
+ }
|
||
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6);
|
||
+ }
|
||
sqlite3_free(zOut);
|
||
return SQLITE_OK;
|
||
}else{
|
||
@@ -38142,7 +38492,7 @@ static int winFullPathname(
|
||
*/
|
||
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
|
||
HANDLE h;
|
||
-#if defined(__CYGWIN__)
|
||
+#if 0
|
||
int nFull = pVfs->mxPathname+1;
|
||
char *zFull = sqlite3MallocZero( nFull );
|
||
void *zConverted = 0;
|
||
@@ -38158,7 +38508,8 @@ static void *winDlOpen(sqlite3_vfs *pVfs
|
||
zConverted = winConvertFromUtf8Filename(zFull);
|
||
sqlite3_free(zFull);
|
||
#else
|
||
- void *zConverted = winConvertFromUtf8Filename(zFilename);
|
||
+ WCHAR buf[MAX_PATH];
|
||
+ void *zConverted = winConvertFromUtf8Filename(zFilename, buf);
|
||
UNUSED_PARAMETER(pVfs);
|
||
#endif
|
||
if( zConverted==0 ){
|
||
@@ -38178,7 +38529,9 @@ static void *winDlOpen(sqlite3_vfs *pVfs
|
||
}
|
||
#endif
|
||
OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
|
||
- sqlite3_free(zConverted);
|
||
+ if( zConverted!=buf ){
|
||
+ sqlite3_free(zConverted);
|
||
+ }
|
||
return (void*)h;
|
||
}
|
||
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
|
||
@@ -38216,31 +38569,31 @@ static int winRandomness(sqlite3_vfs *pV
|
||
n = nBuf;
|
||
memset(zBuf, 0, nBuf);
|
||
#else
|
||
- if( sizeof(SYSTEMTIME)<=nBuf-n ){
|
||
+ if( (int)sizeof(SYSTEMTIME)<=nBuf-n ){
|
||
SYSTEMTIME x;
|
||
osGetSystemTime(&x);
|
||
memcpy(&zBuf[n], &x, sizeof(x));
|
||
n += sizeof(x);
|
||
}
|
||
- if( sizeof(DWORD)<=nBuf-n ){
|
||
+ if( (int)sizeof(DWORD)<=nBuf-n ){
|
||
DWORD pid = osGetCurrentProcessId();
|
||
memcpy(&zBuf[n], &pid, sizeof(pid));
|
||
n += sizeof(pid);
|
||
}
|
||
#if SQLITE_OS_WINRT
|
||
- if( sizeof(ULONGLONG)<=nBuf-n ){
|
||
+ if( (int)sizeof(ULONGLONG)<=nBuf-n ){
|
||
ULONGLONG cnt = osGetTickCount64();
|
||
memcpy(&zBuf[n], &cnt, sizeof(cnt));
|
||
n += sizeof(cnt);
|
||
}
|
||
#else
|
||
- if( sizeof(DWORD)<=nBuf-n ){
|
||
+ if( (int)sizeof(DWORD)<=nBuf-n ){
|
||
DWORD cnt = osGetTickCount();
|
||
memcpy(&zBuf[n], &cnt, sizeof(cnt));
|
||
n += sizeof(cnt);
|
||
}
|
||
#endif
|
||
- if( sizeof(LARGE_INTEGER)<=nBuf-n ){
|
||
+ if( (int)sizeof(LARGE_INTEGER)<=nBuf-n ){
|
||
LARGE_INTEGER i;
|
||
osQueryPerformanceCounter(&i);
|
||
memcpy(&zBuf[n], &i, sizeof(i));
|
||
@@ -38367,6 +38720,10 @@ static int winGetLastError(sqlite3_vfs *
|
||
return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
|
||
}
|
||
|
||
+#if SQLITE_OS_UNIX && !defined(SQLITE_AMALGAMATION)
|
||
+SQLITE_API int sqlite3_os_unix_init(void);
|
||
+#endif
|
||
+
|
||
/*
|
||
** Initialize and deinitialize the operating system interface.
|
||
*/
|
||
@@ -38420,11 +38777,15 @@ SQLITE_API int sqlite3_os_init(void){
|
||
winGetSystemCall, /* xGetSystemCall */
|
||
winNextSystemCall, /* xNextSystemCall */
|
||
};
|
||
+#ifdef _WIN32
|
||
+ int i;
|
||
+ HMODULE module;
|
||
+#endif
|
||
#endif
|
||
|
||
/* Double-check that the aSyscall[] array has been constructed
|
||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||
- assert( ArraySize(aSyscall)==77 );
|
||
+ assert( ArraySize(aSyscall)==83 );
|
||
|
||
/* get memory map allocation granularity */
|
||
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
|
||
@@ -38436,8 +38797,46 @@ SQLITE_API int sqlite3_os_init(void){
|
||
assert( winSysInfo.dwAllocationGranularity>0 );
|
||
assert( winSysInfo.dwPageSize>0 );
|
||
|
||
+#ifdef _WIN32
|
||
+ module = osGetModuleHandleW(L"CYGWIN1.DLL");
|
||
+ if( !module){
|
||
+ module = osGetModuleHandleW(L"MSYS-2.0.DLL");
|
||
+ }
|
||
+ if( !module){
|
||
+ module = osGetModuleHandleW(L"MSYS-1.0.DLL");
|
||
+ }
|
||
+ if( module ){
|
||
+ for( i=78; i<ArraySize(aSyscall); ++i ){
|
||
+ aSyscall[i].pCurrent = (SYSCALL) osGetProcAddressA(module,
|
||
+ aSyscall[i].zName);
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
+
|
||
+#if SQLITE_OS_UNIX
|
||
+ sqlite3_os_unix_init();
|
||
+#endif
|
||
+
|
||
sqlite3_vfs_register(&winVfs, 1);
|
||
|
||
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||
+ if( cygwin_conv_path ){
|
||
+ WCHAR buf[MAX_PATH];
|
||
+ cygwin_conv_path(CCP_POSIX_TO_WIN_W, "/usr/bin",
|
||
+ buf, MAX_PATH*sizeof(WCHAR));
|
||
+ osSetDllDirectoryW(buf);
|
||
+#ifdef _WIN32
|
||
+ }else if( cygwin_conv_to_full_win32_path ){
|
||
+ WCHAR buf[MAX_PATH];
|
||
+ char *buf1 = (char *)buf;
|
||
+ int i = MAX_PATH;
|
||
+ cygwin_conv_to_full_win32_path("/usr/bin", buf1);
|
||
+ while(--i>=0) buf[i] = buf1[i];
|
||
+ osSetDllDirectoryW(buf);
|
||
+#endif
|
||
+ }
|
||
+#endif
|
||
+
|
||
#if defined(SQLITE_WIN32_HAS_WIDE)
|
||
sqlite3_vfs_register(&winLongPathVfs, 0);
|
||
#endif
|
||
@@ -38446,6 +38845,19 @@ SQLITE_API int sqlite3_os_init(void){
|
||
}
|
||
|
||
SQLITE_API int sqlite3_os_end(void){
|
||
+#ifdef _WIN32
|
||
+ int i;
|
||
+#endif
|
||
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||
+ if( cygwin_conv_path || cygwin_conv_to_full_win32_path){
|
||
+ osSetDllDirectoryW(0);
|
||
+ }
|
||
+#endif
|
||
+#ifdef _WIN32
|
||
+ for( i=78; i<ArraySize(aSyscall); ++i ){
|
||
+ aSyscall[i].pCurrent = 0;
|
||
+ }
|
||
+#endif
|
||
#if SQLITE_OS_WINRT
|
||
if( sleepObj!=NULL ){
|
||
osCloseHandle(sleepObj);
|
||
@@ -64319,7 +64731,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdb
|
||
pOp->p3 = p3;
|
||
pOp->p4.p = 0;
|
||
pOp->p4type = P4_NOTUSED;
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
pOp->zComment = 0;
|
||
#endif
|
||
#ifdef SQLITE_DEBUG
|
||
@@ -64733,7 +65145,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(
|
||
pOut->p4type = P4_NOTUSED;
|
||
pOut->p4.p = 0;
|
||
pOut->p5 = 0;
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
pOut->zComment = 0;
|
||
#endif
|
||
#ifdef SQLITE_VDBE_COVERAGE
|
||
@@ -64902,7 +65314,7 @@ static void vdbeFreeOpArray(sqlite3 *db,
|
||
Op *pOp;
|
||
for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
|
||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
sqlite3DbFree(db, pOp->zComment);
|
||
#endif
|
||
}
|
||
@@ -65025,7 +65437,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetP4KeyI
|
||
P4_KEYINFO);
|
||
}
|
||
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
/*
|
||
** Change the comment on the most recently coded instruction. Or
|
||
** insert a No-op and add the comment to that new instruction. This
|
||
@@ -65098,7 +65510,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(
|
||
}
|
||
}
|
||
|
||
-#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
|
||
+#if 1
|
||
/*
|
||
** Return an integer value for one of the parameters to the opcode pOp
|
||
** determined by character c.
|
||
@@ -65387,8 +65799,12 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(F
|
||
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
|
||
if( pOut==0 ) pOut = stdout;
|
||
zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
- displayComment(pOp, zP4, zCom, sizeof(zCom));
|
||
+#if 1
|
||
+ if( sqlite3GlobalConfig.bVdbeComments ){
|
||
+ displayComment(pOp, zP4, zCom, sizeof(zCom));
|
||
+ }else{
|
||
+ zCom[0] = 0;
|
||
+ }
|
||
#else
|
||
zCom[0] = 0;
|
||
#endif
|
||
@@ -65633,15 +66049,19 @@ SQLITE_PRIVATE int sqlite3VdbeList(
|
||
sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
|
||
pMem->enc = SQLITE_UTF8;
|
||
pMem++;
|
||
-
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
- if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
|
||
- assert( p->db->mallocFailed );
|
||
- return SQLITE_ERROR;
|
||
+
|
||
+#if 1
|
||
+ if( sqlite3GlobalConfig.bVdbeComments ){
|
||
+ if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
|
||
+ assert( p->db->mallocFailed );
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ pMem->flags = MEM_Str|MEM_Term;
|
||
+ pMem->n = displayComment(pOp, zP4, pMem->z, 500);
|
||
+ pMem->enc = SQLITE_UTF8;
|
||
+ }else{
|
||
+ pMem->flags = MEM_Null;
|
||
}
|
||
- pMem->flags = MEM_Str|MEM_Term;
|
||
- pMem->n = displayComment(pOp, zP4, pMem->z, 500);
|
||
- pMem->enc = SQLITE_UTF8;
|
||
#else
|
||
pMem->flags = MEM_Null; /* Comment */
|
||
#endif
|
||
@@ -77736,7 +78156,7 @@ static int vdbeSorterMapFile(SortSubtask
|
||
int rc = SQLITE_OK;
|
||
if( pFile->iEof<=(i64)(pTask->pSorter->db->nMaxSorterMmap) ){
|
||
sqlite3_file *pFd = pFile->pFd;
|
||
- if( pFd->pMethods->iVersion>=3 ){
|
||
+ if( pFd->pMethods->iVersion>=3 && pFd->pMethods->xFetch ){
|
||
rc = sqlite3OsFetch(pFd, 0, (int)pFile->iEof, (void**)pp);
|
||
testcase( rc!=SQLITE_OK );
|
||
}
|
||
@@ -84051,7 +84471,8 @@ SQLITE_PRIVATE void sqlite3ExprCacheStor
|
||
int idxLru;
|
||
struct yColCache *p;
|
||
|
||
- assert( iReg>0 ); /* Register numbers are always positive */
|
||
+ /* Unless an error has occurred, register numbers are always positive. */
|
||
+ assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed );
|
||
assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */
|
||
|
||
/* The SQLITE_ColumnCache flag disables the column cache. This is used
|
||
@@ -89894,7 +90315,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(
|
||
while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){}
|
||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
if( pParse->nTableLock>0 && db->init.busy==0 ){
|
||
sqlite3UserAuthInit(db);
|
||
if( db->auth.authLevel<UAUTH_User ){
|
||
@@ -90020,7 +90441,7 @@ SQLITE_PRIVATE void sqlite3NestedParse(P
|
||
pParse->nested--;
|
||
}
|
||
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
/*
|
||
** Return TRUE if zTable is the name of the system table that stores the
|
||
** list of users and their access credentials.
|
||
@@ -90052,7 +90473,7 @@ SQLITE_PRIVATE Table *sqlite3FindTable(s
|
||
|
||
/* All mutexes are required for schema access. Make sure we hold them. */
|
||
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
/* Only the admin user is allowed to know that the sqlite_user table
|
||
** exists */
|
||
if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){
|
||
@@ -92659,7 +93080,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex
|
||
assert( pParse->nErr==0 );
|
||
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
|
||
&& db->init.busy==0
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
&& sqlite3UserAuthTable(pTab->zName)==0
|
||
#endif
|
||
&& sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){
|
||
@@ -97090,7 +97511,7 @@ SQLITE_PRIVATE void sqlite3RegisterGloba
|
||
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
|
||
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
|
||
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
|
||
#endif
|
||
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
|
||
@@ -101066,9 +101487,11 @@ struct sqlite3_api_routines {
|
||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||
#define sqlite3_complete sqlite3_api->complete
|
||
#define sqlite3_complete16 sqlite3_api->complete16
|
||
-#define sqlite3_create_collation sqlite3_api->create_collation
|
||
+#undef sqlite3_create_collation
|
||
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||
-#define sqlite3_create_function sqlite3_api->create_function
|
||
+#undef sqlite3_create_function
|
||
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||
#define sqlite3_create_module sqlite3_api->create_module
|
||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||
@@ -101205,7 +101628,8 @@ struct sqlite3_api_routines {
|
||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||
-#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||
+#undef sqlite3_wal_checkpoint
|
||
+#define sqlite3_wal_checkpoint(a,b) sqlite3_api->wal_checkpoint(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0)
|
||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||
@@ -101260,7 +101684,7 @@ struct sqlite3_api_routines {
|
||
/************** Continuing where we left off in loadext.c ********************/
|
||
/* #include <string.h> */
|
||
|
||
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) || !defined(SQLITE_OMIT_AUTOINIT)
|
||
|
||
/*
|
||
** Some API routines are omitted when various features are
|
||
@@ -101635,7 +102059,11 @@ static const sqlite3_api_routines sqlite
|
||
sqlite3_bind_blob64,
|
||
sqlite3_bind_text64,
|
||
sqlite3_cancel_auto_extension,
|
||
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||
sqlite3_load_extension,
|
||
+#else
|
||
+ 0,
|
||
+#endif
|
||
sqlite3_malloc64,
|
||
sqlite3_msize,
|
||
sqlite3_realloc64,
|
||
@@ -101644,7 +102072,9 @@ static const sqlite3_api_routines sqlite
|
||
sqlite3_result_text64,
|
||
sqlite3_strglob
|
||
};
|
||
+#endif
|
||
|
||
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||
/*
|
||
** Attempt to load an SQLite extension library contained in the file
|
||
** zFile. The entry point is zProc. zProc may be 0 in which case a
|
||
@@ -101671,18 +102101,17 @@ static int sqlite3LoadExtension(
|
||
char *zAltEntry = 0;
|
||
void **aHandle;
|
||
int nMsg = 300 + sqlite3Strlen30(zFile);
|
||
- int ii;
|
||
|
||
/* Shared library endings to try if zFile cannot be loaded as written */
|
||
- static const char *azEndings[] = {
|
||
-#if SQLITE_OS_WIN
|
||
+ static const char azEnding[] =
|
||
+#if SQLITE_OS_WIN || defined(__CYGWIN__)
|
||
"dll"
|
||
#elif defined(__APPLE__)
|
||
"dylib"
|
||
#else
|
||
"so"
|
||
#endif
|
||
- };
|
||
+ ;
|
||
|
||
|
||
if( pzErrMsg ) *pzErrMsg = 0;
|
||
@@ -101703,14 +102132,32 @@ static int sqlite3LoadExtension(
|
||
zEntry = zProc ? zProc : "sqlite3_extension_init";
|
||
|
||
handle = sqlite3OsDlOpen(pVfs, zFile);
|
||
-#if SQLITE_OS_UNIX || SQLITE_OS_WIN
|
||
- for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
|
||
- char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
|
||
+ if( handle==0 ){
|
||
+ char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEnding);
|
||
if( zAltFile==0 ) return SQLITE_NOMEM;
|
||
handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||
sqlite3_free(zAltFile);
|
||
- }
|
||
+#if defined(_WIN32) || defined(__CYGWIN__)
|
||
+ if( handle==0 ){
|
||
+ zAltFile = sqlite3_mprintf("sqlite3%s.%s", zFile, azEnding);
|
||
+ if( zAltFile==0 ) return SQLITE_NOMEM;
|
||
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||
+ sqlite3_free(zAltFile);
|
||
+ }
|
||
+ if( handle==0 ){
|
||
+ zAltFile = sqlite3_mprintf("cygsqlite3%s-0.%s", zFile, azEnding);
|
||
+ if( zAltFile==0 ) return SQLITE_NOMEM;
|
||
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||
+ sqlite3_free(zAltFile);
|
||
+ }
|
||
+ if( handle==0 ){
|
||
+ zAltFile = sqlite3_mprintf("msys-sqlite3%s-0.%s", zFile, azEnding);
|
||
+ if( zAltFile==0 ) return SQLITE_NOMEM;
|
||
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
|
||
+ sqlite3_free(zAltFile);
|
||
+ }
|
||
#endif
|
||
+ }
|
||
if( handle==0 ){
|
||
if( pzErrMsg ){
|
||
*pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
|
||
@@ -101841,17 +102288,6 @@ SQLITE_API int sqlite3_enable_load_exten
|
||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||
|
||
/*
|
||
-** The auto-extension code added regardless of whether or not extension
|
||
-** loading is supported. We need a dummy sqlite3Apis pointer for that
|
||
-** code if regular extension loading is not available. This is that
|
||
-** dummy pointer.
|
||
-*/
|
||
-#ifdef SQLITE_OMIT_LOAD_EXTENSION
|
||
-static const sqlite3_api_routines sqlite3Apis = { 0 };
|
||
-#endif
|
||
-
|
||
-
|
||
-/*
|
||
** The following object holds the list of automatically loaded
|
||
** extensions.
|
||
**
|
||
@@ -102160,7 +102596,7 @@ static const struct sPragmaNames {
|
||
/* ePragFlag: */ 0,
|
||
/* iArg: */ SQLITE_CountRows },
|
||
#endif
|
||
-#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
|
||
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && defined(_WIN32)
|
||
{ /* zName: */ "data_store_directory",
|
||
/* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY,
|
||
/* ePragFlag: */ 0,
|
||
@@ -103290,7 +103726,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
||
break;
|
||
}
|
||
|
||
-#if SQLITE_OS_WIN
|
||
+#if defined(_WIN32)
|
||
/*
|
||
** PRAGMA data_store_directory
|
||
** PRAGMA data_store_directory = ""|"directory_name"
|
||
@@ -103416,7 +103852,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
||
** in auto-commit mode. */
|
||
mask &= ~(SQLITE_ForeignKeys);
|
||
}
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
if( db->auth.authLevel==UAUTH_User ){
|
||
/* Do not allow non-admin users to modify the schema arbitrarily */
|
||
mask &= ~(SQLITE_WriteSchema);
|
||
@@ -106480,7 +106916,7 @@ static void generateSortTail(
|
||
int nSortData; /* Trailing values to read from sorter */
|
||
int i;
|
||
int bSeq; /* True if sorter record includes seq. no. */
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
struct ExprList_item *aOutEx = p->pEList->a;
|
||
#endif
|
||
|
||
@@ -109447,7 +109883,7 @@ static int selectExpander(Walker *pWalke
|
||
/* A sub-query in the FROM clause of a SELECT */
|
||
assert( pSel!=0 );
|
||
assert( pFrom->pTab==0 );
|
||
- sqlite3WalkSelect(pWalker, pSel);
|
||
+ if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
|
||
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
||
if( pTab==0 ) return WRC_Abort;
|
||
pTab->nRef = 1;
|
||
@@ -111850,7 +112286,7 @@ static int codeTriggerProgram(
|
||
return 0;
|
||
}
|
||
|
||
-#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||
+#if 1
|
||
/*
|
||
** This function is used to add VdbeComment() annotations to a VDBE
|
||
** program. It is not used in production code, only for debugging.
|
||
@@ -127005,6 +127441,13 @@ SQLITE_API int sqlite3_config(int op, ..
|
||
break;
|
||
}
|
||
|
||
+ case 32: /* SQLITE_CONFIG_EXPLAIN_COMMENTS */
|
||
+ case 64: /* SQLITE_CONFIG_EXPLAIN_COMMENTS */ {
|
||
+ /* Enable VDBE commenting (cannot be switched off) */
|
||
+ sqlite3GlobalConfig.bVdbeComments = 1;
|
||
+ break;
|
||
+ }
|
||
+
|
||
default: {
|
||
rc = SQLITE_ERROR;
|
||
break;
|
||
@@ -127510,7 +127953,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAnd
|
||
sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */
|
||
sqlite3ValueFree(db->pErr);
|
||
sqlite3CloseExtensions(db);
|
||
-#if SQLITE_USER_AUTHENTICATION
|
||
+#ifdef SQLITE_USER_AUTHENTICATION
|
||
sqlite3_free(db->auth.zAuthUser);
|
||
sqlite3_free(db->auth.zAuthPW);
|
||
#endif
|
||
@@ -128001,6 +128444,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
|
||
/*
|
||
** Create new user functions.
|
||
*/
|
||
+#undef sqlite3_create_function
|
||
SQLITE_API int sqlite3_create_function(
|
||
sqlite3 *db,
|
||
const char *zFunc,
|
||
@@ -128388,6 +128832,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2
|
||
** to contains a zero-length string, all attached databases are
|
||
** checkpointed.
|
||
*/
|
||
+#undef sqlite3_wal_checkpoint
|
||
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
|
||
/* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to
|
||
** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */
|
||
@@ -129350,6 +129795,7 @@ SQLITE_API int sqlite3_open16(
|
||
/*
|
||
** Register a new collation sequence with the database handle db.
|
||
*/
|
||
+#undef sqlite3_create_collation
|
||
SQLITE_API int sqlite3_create_collation(
|
||
sqlite3* db,
|
||
const char *zName,
|
||
@@ -132794,11 +133240,7 @@ static int fts3CreateMethod(
|
||
** support estimatedRows. In that case this function is a no-op.
|
||
*/
|
||
static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||
-#if SQLITE_VERSION_NUMBER>=3008002
|
||
- if( sqlite3_libversion_number()>=3008002 ){
|
||
- pIdxInfo->estimatedRows = nRow;
|
||
- }
|
||
-#endif
|
||
+ pIdxInfo->estimatedRows = nRow;
|
||
}
|
||
|
||
/*
|
||
@@ -135125,6 +135567,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlit
|
||
|
||
#ifndef SQLITE_DISABLE_FTS3_UNICODE
|
||
|| sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
|
||
+ || sqlite3Fts3HashInsert(pHash, "unicode", 8, (void *)pUnicode)
|
||
#endif
|
||
#ifdef SQLITE_ENABLE_ICU
|
||
|| (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
|
||
@@ -147526,8 +147969,12 @@ static int fts3SnippetText(
|
||
** required. They are required if (a) this is not the first fragment,
|
||
** or (b) this fragment does not begin at position 0 of its column.
|
||
*/
|
||
- if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){
|
||
- rc = fts3StringAppend(pOut, zEllipsis, -1);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ if( iPos>0 || iFragment>0 ){
|
||
+ rc = fts3StringAppend(pOut, zEllipsis, -1);
|
||
+ }else if( iBegin ){
|
||
+ rc = fts3StringAppend(pOut, zDoc, iBegin);
|
||
+ }
|
||
}
|
||
if( rc!=SQLITE_OK || iCurrent<iPos ) continue;
|
||
}
|
||
@@ -148808,84 +149255,91 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsal
|
||
0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
|
||
0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
|
||
0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
|
||
- 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
|
||
- 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
|
||
- 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
|
||
- 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
|
||
- 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
|
||
- 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
|
||
- 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
|
||
- 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
|
||
- 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
|
||
- 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
|
||
- 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
|
||
- 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
|
||
- 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
|
||
- 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
|
||
- 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
|
||
- 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
|
||
- 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
|
||
- 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
|
||
- 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
|
||
- 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
|
||
- 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
|
||
- 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
|
||
- 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
|
||
- 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
|
||
- 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
|
||
- 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
|
||
- 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
|
||
- 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
|
||
- 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
|
||
- 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
|
||
- 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
|
||
- 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
|
||
- 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
|
||
- 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
|
||
- 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
|
||
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163403,
|
||
+ 0x00164437, 0x0017CC02, 0x0018001D, 0x00187802, 0x00192C15,
|
||
+ 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, 0x001B9C07,
|
||
+ 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, 0x001CC01B,
|
||
+ 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804, 0x00206C09,
|
||
+ 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, 0x00217801,
|
||
+ 0x00239020, 0x0024E803, 0x0024F812, 0x00254407, 0x00258804,
|
||
+ 0x0025C001, 0x00260403, 0x0026F001, 0x0026F807, 0x00271C02,
|
||
+ 0x00272C03, 0x00275C01, 0x00278802, 0x0027C802, 0x0027E802,
|
||
+ 0x00280403, 0x0028F001, 0x0028F805, 0x00291C02, 0x00292C03,
|
||
+ 0x00294401, 0x0029C002, 0x0029D401, 0x002A0403, 0x002AF001,
|
||
+ 0x002AF808, 0x002B1C03, 0x002B2C03, 0x002B8802, 0x002BC002,
|
||
+ 0x002C0403, 0x002CF001, 0x002CF807, 0x002D1C02, 0x002D2C03,
|
||
+ 0x002D5802, 0x002D8802, 0x002DC001, 0x002E0801, 0x002EF805,
|
||
+ 0x002F1803, 0x002F2804, 0x002F5C01, 0x002FCC08, 0x00300004,
|
||
+ 0x0030F807, 0x00311803, 0x00312804, 0x00315402, 0x00318802,
|
||
+ 0x0031FC01, 0x00320403, 0x0032F001, 0x0032F807, 0x00331803,
|
||
+ 0x00332804, 0x00335402, 0x00338802, 0x00340403, 0x0034F807,
|
||
+ 0x00351803, 0x00352804, 0x00355C01, 0x00358802, 0x0035E401,
|
||
+ 0x00360802, 0x00372801, 0x00373C06, 0x00375801, 0x00376008,
|
||
+ 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01, 0x00391C09,
|
||
+ 0x00396802, 0x003AC401, 0x003AD006, 0x003AEC02, 0x003B2006,
|
||
+ 0x003C041F, 0x003CD00C, 0x003DC417, 0x003E340B, 0x003E6424,
|
||
+ 0x003EF80F, 0x003F380D, 0x0040AC14, 0x00412806, 0x00415804,
|
||
+ 0x00417803, 0x00418803, 0x00419C07, 0x0041C404, 0x0042080C,
|
||
+ 0x00423C01, 0x00426806, 0x0043EC01, 0x004D740C, 0x004E400A,
|
||
+ 0x00500001, 0x0059B402, 0x005A0001, 0x005A6C02, 0x005BAC03,
|
||
+ 0x005C4803, 0x005CC805, 0x005D4802, 0x005DC802, 0x005ED023,
|
||
+ 0x005F6004, 0x005F7401, 0x0060000F, 0x0062A401, 0x0064800C,
|
||
+ 0x0064C00C, 0x00650001, 0x00651002, 0x0066C011, 0x00672002,
|
||
+ 0x00677822, 0x00685C05, 0x00687802, 0x0069540A, 0x0069801D,
|
||
+ 0x0069FC01, 0x006A8007, 0x006AA006, 0x006AC00F, 0x006C0005,
|
||
+ 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E,
|
||
+ 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, 0x00730008,
|
||
+ 0x00734019, 0x0073B401, 0x0073C803, 0x0073E002, 0x00770036,
|
||
0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
|
||
- 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
|
||
- 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
|
||
+ 0x007FB403, 0x007FF402, 0x00800065, 0x0081980A, 0x0081E805,
|
||
+ 0x00822805, 0x0082801E, 0x00834021, 0x00840002, 0x00840C04,
|
||
0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
|
||
0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
|
||
- 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
|
||
- 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
|
||
- 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
|
||
- 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
|
||
- 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
|
||
- 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
|
||
- 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
|
||
- 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
|
||
- 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
|
||
- 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
|
||
- 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
|
||
- 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
|
||
- 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
|
||
- 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
|
||
- 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
|
||
- 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
|
||
- 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
|
||
- 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
|
||
- 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
|
||
- 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
|
||
- 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
|
||
- 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
|
||
- 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
|
||
+ 0x00852804, 0x00853C01, 0x0086426B, 0x00900027, 0x0091000B,
|
||
+ 0x0092704E, 0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE6022,
|
||
+ 0x00AEF40C, 0x00AF2808, 0x00B39406, 0x00B3BC03, 0x00B3E404,
|
||
+ 0x00B3F802, 0x00B5C001, 0x00B5FC01, 0x00B7804F, 0x00B8C013,
|
||
+ 0x00BA001A, 0x00BA6C59, 0x00BC00D6, 0x00BFC00C, 0x00C00005,
|
||
+ 0x00C02019, 0x00C0A807, 0x00C0D802, 0x00C0F403, 0x00C26404,
|
||
+ 0x00C28001, 0x00C3EC01, 0x00C64002, 0x00C6580A, 0x00C70024,
|
||
+ 0x00C8001F, 0x00C8A81E, 0x00C94001, 0x00C98020, 0x00CA2827,
|
||
+ 0x00CB003F, 0x00CC0100, 0x01370040, 0x02924037, 0x0293F802,
|
||
+ 0x02983403, 0x0299BC10, 0x029A7C01, 0x029BC008, 0x029C0017,
|
||
+ 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801, 0x02A02C01,
|
||
+ 0x02A08C09, 0x02A0D804, 0x02A1D004, 0x02A20002, 0x02A2D011,
|
||
+ 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A4980A, 0x02A51C0D,
|
||
+ 0x02A57C01, 0x02A60004, 0x02A6CC1B, 0x02A77802, 0x02A79401,
|
||
+ 0x02A8A40E, 0x02A90C01, 0x02A93002, 0x02A97004, 0x02A9DC03,
|
||
+ 0x02A9EC03, 0x02AAC001, 0x02AAC803, 0x02AADC02, 0x02AAF802,
|
||
+ 0x02AB0401, 0x02AB7802, 0x02ABAC07, 0x02ABD402, 0x02AD6C01,
|
||
+ 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, 0x037FFC01,
|
||
+ 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802, 0x03F7F002,
|
||
+ 0x03F8001A, 0x03F8800E, 0x03F8C023, 0x03F95013, 0x03F9A004,
|
||
+ 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06, 0x03FD6C0B,
|
||
+ 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003, 0x0404DC09,
|
||
+ 0x0405E411, 0x04063001, 0x0406400C, 0x04068001, 0x0407402E,
|
||
+ 0x040B8001, 0x040DD805, 0x040E7C01, 0x040F4001, 0x0415BC01,
|
||
+ 0x04215C01, 0x0421DC02, 0x04247C01, 0x0424FC01, 0x04280403,
|
||
0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
|
||
- 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
|
||
- 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
|
||
- 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
|
||
- 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
|
||
- 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
|
||
- 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
|
||
- 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
|
||
- 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
|
||
- 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
|
||
- 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
|
||
- 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
|
||
- 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
|
||
- 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
|
||
- 0x380400F0,
|
||
+ 0x0429FC01, 0x042B2001, 0x042B9402, 0x042BC007, 0x042CE407,
|
||
+ 0x042E6404, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012,
|
||
+ 0x04440003, 0x04449C0E, 0x04450004, 0x0445CC03, 0x04460003,
|
||
+ 0x0446CC0E, 0x04471404, 0x04473401, 0x0448B012, 0x044B7C0C,
|
||
+ 0x044C0403, 0x044CF001, 0x044CF807, 0x044D1C02, 0x044D2C03,
|
||
+ 0x044D5C01, 0x044D8802, 0x044D9807, 0x044DC005, 0x0452C014,
|
||
+ 0x04531801, 0x0456BC07, 0x0456E012, 0x0458C014, 0x045AAC0D,
|
||
+ 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010, 0x05AD1002,
|
||
+ 0x05BD442E, 0x05BE3C04, 0x06F27008, 0x074000F6, 0x07440027,
|
||
+ 0x0744A4B5, 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01,
|
||
+ 0x075BEC01, 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01,
|
||
+ 0x075E2401, 0x075EA401, 0x075F0C01, 0x07A34007, 0x07BBC002,
|
||
+ 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F, 0x07C3040F,
|
||
+ 0x07C34425, 0x07C4401F, 0x07C4C03C, 0x07C5C02B, 0x07C7981D,
|
||
+ 0x07C8402B, 0x07C90009, 0x07C94002, 0x07CC002D, 0x07CCC04E,
|
||
+ 0x07CE004F, 0x07CF5024, 0x07D000FF, 0x07D4004B, 0x07D5402A,
|
||
+ 0x07D5EC29, 0x07D6949E, 0x07D9148B, 0x07DB800D, 0x07DBC004,
|
||
+ 0x07DC0074, 0x07DE0055, 0x07E0000C, 0x07E04038, 0x07E1400A,
|
||
+ 0x07E18028, 0x07E2401E, 0x38000401, 0x38008060, 0x380400F0,
|
||
};
|
||
static const unsigned int aAscii[4] = {
|
||
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
|
||
@@ -148924,7 +149378,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsal
|
||
** uppercase letter are undefined.
|
||
*/
|
||
static int remove_diacritic(int c){
|
||
- unsigned short aDia[] = {
|
||
+ static const unsigned short aDia[] = {
|
||
0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
|
||
2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
|
||
2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
|
||
@@ -148939,7 +149393,7 @@ static int remove_diacritic(int c){
|
||
62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
|
||
62924, 63050, 63082, 63274, 63390,
|
||
};
|
||
- char aChar[] = {
|
||
+ static const char aChar[] = {
|
||
'\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
|
||
'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
|
||
's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
|
||
@@ -149017,8 +149471,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
||
} aEntry[] = {
|
||
{65, 14, 26}, {181, 64, 1}, {192, 14, 23},
|
||
{216, 14, 7}, {256, 1, 48}, {306, 1, 6},
|
||
- {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
|
||
- {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
|
||
+ {313, 1, 16}, {330, 1, 46}, {376, 126, 1},
|
||
+ {377, 1, 6}, {383, 114, 1}, {385, 50, 1},
|
||
{386, 1, 4}, {390, 44, 1}, {391, 0, 1},
|
||
{393, 42, 2}, {395, 0, 1}, {398, 32, 1},
|
||
{399, 38, 1}, {400, 40, 1}, {401, 0, 1},
|
||
@@ -149031,44 +149485,46 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
||
{440, 0, 1}, {444, 0, 1}, {452, 2, 1},
|
||
{453, 0, 1}, {455, 2, 1}, {456, 0, 1},
|
||
{458, 2, 1}, {459, 1, 18}, {478, 1, 18},
|
||
- {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
|
||
- {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
|
||
+ {497, 2, 1}, {498, 1, 4}, {502, 132, 1},
|
||
+ {503, 144, 1}, {504, 1, 40}, {544, 120, 1},
|
||
{546, 1, 18}, {570, 70, 1}, {571, 0, 1},
|
||
- {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
|
||
- {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
|
||
+ {573, 118, 1}, {574, 68, 1}, {577, 0, 1},
|
||
+ {579, 116, 1}, {580, 28, 1}, {581, 30, 1},
|
||
{582, 1, 10}, {837, 36, 1}, {880, 1, 4},
|
||
- {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
|
||
- {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
|
||
- {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
|
||
- {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
|
||
- {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
|
||
- {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
|
||
- {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
|
||
- {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
|
||
- {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
|
||
- {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
|
||
- {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
|
||
- {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
|
||
- {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
|
||
- {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
|
||
- {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
|
||
- {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
|
||
- {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
|
||
- {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
|
||
- {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
|
||
- {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
|
||
- {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
|
||
- {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
|
||
- {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
|
||
- {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
|
||
- {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
|
||
- {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
|
||
- {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
|
||
- {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
|
||
- {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
|
||
- {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
|
||
- {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
|
||
- {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
|
||
+ {886, 0, 1}, {895, 36, 1}, {902, 18, 1},
|
||
+ {904, 16, 3}, {908, 26, 1}, {910, 24, 2},
|
||
+ {913, 14, 17}, {931, 14, 9}, {962, 0, 1},
|
||
+ {975, 4, 1}, {976, 150, 1}, {977, 152, 1},
|
||
+ {981, 156, 1}, {982, 154, 1}, {984, 1, 24},
|
||
+ {1008, 146, 1}, {1009, 148, 1}, {1012, 140, 1},
|
||
+ {1013, 138, 1}, {1015, 0, 1}, {1017, 162, 1},
|
||
+ {1018, 0, 1}, {1021, 120, 3}, {1024, 34, 16},
|
||
+ {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54},
|
||
+ {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96},
|
||
+ {1329, 22, 38}, {4256, 66, 38}, {4295, 66, 1},
|
||
+ {4301, 66, 1}, {7680, 1, 150}, {7835, 142, 1},
|
||
+ {7838, 106, 1}, {7840, 1, 96}, {7944, 160, 8},
|
||
+ {7960, 160, 6}, {7976, 160, 8}, {7992, 160, 8},
|
||
+ {8008, 160, 6}, {8025, 161, 8}, {8040, 160, 8},
|
||
+ {8072, 160, 8}, {8088, 160, 8}, {8104, 160, 8},
|
||
+ {8120, 160, 2}, {8122, 136, 2}, {8124, 158, 1},
|
||
+ {8126, 110, 1}, {8136, 134, 4}, {8140, 158, 1},
|
||
+ {8152, 160, 2}, {8154, 130, 2}, {8168, 160, 2},
|
||
+ {8170, 128, 2}, {8172, 162, 1}, {8184, 122, 2},
|
||
+ {8186, 124, 2}, {8188, 158, 1}, {8486, 108, 1},
|
||
+ {8490, 102, 1}, {8491, 104, 1}, {8498, 12, 1},
|
||
+ {8544, 8, 16}, {8579, 0, 1}, {9398, 10, 26},
|
||
+ {11264, 22, 47}, {11360, 0, 1}, {11362, 98, 1},
|
||
+ {11363, 112, 1}, {11364, 100, 1}, {11367, 1, 6},
|
||
+ {11373, 94, 1}, {11374, 96, 1}, {11375, 90, 1},
|
||
+ {11376, 92, 1}, {11378, 0, 1}, {11381, 0, 1},
|
||
+ {11390, 88, 2}, {11392, 1, 100}, {11499, 1, 4},
|
||
+ {11506, 0, 1}, {42560, 1, 46}, {42624, 1, 28},
|
||
+ {42786, 1, 14}, {42802, 1, 62}, {42873, 1, 4},
|
||
+ {42877, 86, 1}, {42878, 1, 10}, {42891, 0, 1},
|
||
+ {42893, 82, 1}, {42896, 1, 4}, {42902, 1, 20},
|
||
+ {42922, 76, 1}, {42923, 72, 1}, {42924, 74, 1},
|
||
+ {42925, 78, 1}, {42928, 84, 1}, {42929, 80, 1},
|
||
{65313, 14, 26},
|
||
};
|
||
static const unsigned short aiOff[] = {
|
||
@@ -149076,12 +149532,13 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
||
37, 38, 40, 48, 63, 64, 69, 71,
|
||
79, 80, 116, 202, 203, 205, 206, 207,
|
||
209, 210, 211, 213, 214, 217, 218, 219,
|
||
- 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
|
||
- 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
|
||
- 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
|
||
- 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
|
||
- 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
|
||
- 65514, 65521, 65527, 65528, 65529,
|
||
+ 775, 7264, 10792, 10795, 23217, 23221, 23228, 23231,
|
||
+ 23254, 23256, 23278, 30204, 54721, 54753, 54754, 54756,
|
||
+ 54787, 54793, 54809, 57153, 57274, 57921, 58019, 58363,
|
||
+ 61722, 65268, 65341, 65373, 65406, 65408, 65410, 65415,
|
||
+ 65424, 65436, 65439, 65450, 65462, 65472, 65476, 65478,
|
||
+ 65480, 65482, 65488, 65506, 65511, 65514, 65521, 65527,
|
||
+ 65528, 65529,
|
||
};
|
||
|
||
int ret = c;
|
||
@@ -149122,6 +149579,9 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold
|
||
else if( c>=66560 && c<66600 ){
|
||
ret = c + 40;
|
||
}
|
||
+ else if( c>=71840 && c<71872 ){
|
||
+ ret = c + 32;
|
||
+ }
|
||
|
||
return ret;
|
||
}
|
||
@@ -150754,11 +151214,7 @@ static int rtreeFilter(
|
||
** support estimatedRows. In that case this function is a no-op.
|
||
*/
|
||
static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||
-#if SQLITE_VERSION_NUMBER>=3008002
|
||
- if( sqlite3_libversion_number()>=3008002 ){
|
||
- pIdxInfo->estimatedRows = nRow;
|
||
- }
|
||
-#endif
|
||
+ pIdxInfo->estimatedRows = nRow;
|
||
}
|
||
|
||
/*
|
||
@@ -153055,7 +153511,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite
|
||
void *pContext; /* sqlite3_user_data() context */
|
||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||
} scalars[] = {
|
||
- {"regexp", 2, SQLITE_ANY, 0, icuRegexpFunc},
|
||
+ {"regexp", 2, SQLITE_UTF8, 0, icuRegexpFunc},
|
||
|
||
{"lower", 1, SQLITE_UTF16, 0, icuCaseFunc16},
|
||
{"lower", 2, SQLITE_UTF16, 0, icuCaseFunc16},
|
||
@@ -153078,8 +153534,8 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite
|
||
|
||
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
||
struct IcuScalar *p = &scalars[i];
|
||
- rc = sqlite3_create_function(
|
||
- db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0
|
||
+ rc = sqlite3_create_function_v2(
|
||
+ db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0, 0
|
||
);
|
||
}
|
||
|
||
--- origsrc/sqlite-autoconf-3080802/sqlite3.h 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/sqlite3.h 2015-01-31 00:31:56.591154300 +0100
|
||
@@ -4157,6 +4157,8 @@ SQLITE_API int sqlite3_create_function(
|
||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||
void (*xFinal)(sqlite3_context*)
|
||
);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
||
SQLITE_API int sqlite3_create_function16(
|
||
sqlite3 *db,
|
||
const void *zFunctionName,
|
||
@@ -4642,6 +4644,8 @@ SQLITE_API int sqlite3_create_collation(
|
||
void *pArg,
|
||
int(*xCompare)(void*,int,const void*,int,const void*)
|
||
);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
||
SQLITE_API int sqlite3_create_collation_v2(
|
||
sqlite3*,
|
||
const char *zName,
|
||
@@ -7282,6 +7286,8 @@ SQLITE_API int sqlite3_wal_autocheckpoin
|
||
** complication) of [sqlite3_wal_checkpoint_v2()].
|
||
*/
|
||
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
||
+/* A macro is more efficient than a wrapped call */
|
||
+#define sqlite3_wal_checkpoint(a,b) sqlite3_wal_checkpoint_v2(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0);
|
||
|
||
/*
|
||
** CAPI3REF: Checkpoint a database
|
||
--- origsrc/sqlite-autoconf-3080802/sqlite3ext.h 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/sqlite3ext.h 2015-01-31 00:31:56.601154900 +0100
|
||
@@ -326,9 +326,11 @@ struct sqlite3_api_routines {
|
||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||
#define sqlite3_complete sqlite3_api->complete
|
||
#define sqlite3_complete16 sqlite3_api->complete16
|
||
-#define sqlite3_create_collation sqlite3_api->create_collation
|
||
+#undef sqlite3_create_collation
|
||
+#define sqlite3_create_collation(a,b,c,d,e) sqlite3_create_collation_v2(a,b,c,d,e,0)
|
||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||
-#define sqlite3_create_function sqlite3_api->create_function
|
||
+#undef sqlite3_create_function
|
||
+#define sqlite3_create_function(a,b,c,d,e,f,g,h) sqlite3_create_function_v2(a,b,c,d,e,f,g,h,0)
|
||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||
#define sqlite3_create_module sqlite3_api->create_module
|
||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||
@@ -465,7 +467,8 @@ struct sqlite3_api_routines {
|
||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||
-#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||
+#undef sqlite3_wal_checkpoint
|
||
+#define sqlite3_wal_checkpoint(a,b) sqlite3_api->wal_checkpoint(a,b,SQLITE_CHECKPOINT_PASSIVE,0,0)
|
||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||
--- origsrc/sqlite-autoconf-3080802/sqlite3icu.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/sqlite3icu.c 2015-01-31 00:31:56.612155500 +0100
|
||
@@ -0,0 +1,517 @@
|
||
+/******************************************************************************
|
||
+** This file is an amalgamation of separate C source files from the SQLite
|
||
+** ICU extension. By combining all the individual C
|
||
+** code files into this single large file, the entire code can be compiled
|
||
+** as a one translation unit. This allows many compilers to do optimizations
|
||
+** that would not be possible if the files were compiled separately. It also
|
||
+** makes the code easier to import into other projects.
|
||
+**
|
||
+** This amalgamation was generated on 2014-10-21 15:56:37 UTC.
|
||
+*/
|
||
+/************** Begin file icu.c *********************************************/
|
||
+/*
|
||
+** 2007 May 6
|
||
+**
|
||
+** 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.
|
||
+**
|
||
+*************************************************************************
|
||
+** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
|
||
+**
|
||
+** This file implements an integration between the ICU library
|
||
+** ("International Components for Unicode", an open-source library
|
||
+** for handling unicode data) and SQLite. The integration uses
|
||
+** ICU to provide the following to SQLite:
|
||
+**
|
||
+** * An implementation of the SQL regexp() function (and hence REGEXP
|
||
+** operator) using the ICU uregex_XX() APIs.
|
||
+**
|
||
+** * Implementations of the SQL scalar upper() and lower() functions
|
||
+** for case mapping.
|
||
+**
|
||
+** * Integration of ICU and SQLite collation sequences.
|
||
+**
|
||
+** * An implementation of the LIKE operator that uses ICU to
|
||
+** provide case-independent matching.
|
||
+*/
|
||
+
|
||
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
||
+
|
||
+/* Include ICU headers */
|
||
+#include <unicode/utypes.h>
|
||
+#include <unicode/uregex.h>
|
||
+#include <unicode/ustring.h>
|
||
+#include <unicode/ucol.h>
|
||
+
|
||
+#include <assert.h>
|
||
+
|
||
+#ifndef SQLITE_CORE
|
||
+ #include "sqlite3ext.h"
|
||
+ SQLITE_EXTENSION_INIT1
|
||
+#else
|
||
+ #include "sqlite3.h"
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Maximum length (in bytes) of the pattern in a LIKE or GLOB
|
||
+** operator.
|
||
+*/
|
||
+#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
|
||
+# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Version of sqlite3_free() that is always a function, never a macro.
|
||
+*/
|
||
+static void xFree(void *p){
|
||
+ sqlite3_free(p);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compare two UTF-8 strings for equality where the first string is
|
||
+** a "LIKE" expression. Return true (1) if they are the same and
|
||
+** false (0) if they are different.
|
||
+*/
|
||
+static int icuLikeCompare(
|
||
+ const uint8_t *zPattern, /* LIKE pattern */
|
||
+ const uint8_t *zString, /* The UTF-8 string to compare against */
|
||
+ const UChar32 uEsc /* The escape character */
|
||
+){
|
||
+ static const int MATCH_ONE = (UChar32)'_';
|
||
+ static const int MATCH_ALL = (UChar32)'%';
|
||
+
|
||
+ int iPattern = 0; /* Current byte index in zPattern */
|
||
+ int iString = 0; /* Current byte index in zString */
|
||
+
|
||
+ int prevEscape = 0; /* True if the previous character was uEsc */
|
||
+
|
||
+ while( zPattern[iPattern]!=0 ){
|
||
+
|
||
+ /* Read (and consume) the next character from the input pattern. */
|
||
+ UChar32 uPattern;
|
||
+ U8_NEXT_UNSAFE(zPattern, iPattern, uPattern);
|
||
+ assert(uPattern!=0);
|
||
+
|
||
+ /* There are now 4 possibilities:
|
||
+ **
|
||
+ ** 1. uPattern is an unescaped match-all character "%",
|
||
+ ** 2. uPattern is an unescaped match-one character "_",
|
||
+ ** 3. uPattern is an unescaped escape character, or
|
||
+ ** 4. uPattern is to be handled as an ordinary character
|
||
+ */
|
||
+ if( !prevEscape && uPattern==MATCH_ALL ){
|
||
+ /* Case 1. */
|
||
+ uint8_t c;
|
||
+
|
||
+ /* Skip any MATCH_ALL or MATCH_ONE characters that follow a
|
||
+ ** MATCH_ALL. For each MATCH_ONE, skip one character in the
|
||
+ ** test string.
|
||
+ */
|
||
+ while( (c=zPattern[iPattern]) == MATCH_ALL || c == MATCH_ONE ){
|
||
+ if( c==MATCH_ONE ){
|
||
+ if( zString[iString]==0 ) return 0;
|
||
+ U8_FWD_1_UNSAFE(zString, iString);
|
||
+ }
|
||
+ iPattern++;
|
||
+ }
|
||
+
|
||
+ if( zPattern[iPattern]==0 ) return 1;
|
||
+
|
||
+ while( zString[iString] ){
|
||
+ if( icuLikeCompare(&zPattern[iPattern], &zString[iString], uEsc) ){
|
||
+ return 1;
|
||
+ }
|
||
+ U8_FWD_1_UNSAFE(zString, iString);
|
||
+ }
|
||
+ return 0;
|
||
+
|
||
+ }else if( !prevEscape && uPattern==MATCH_ONE ){
|
||
+ /* Case 2. */
|
||
+ if( zString[iString]==0 ) return 0;
|
||
+ U8_FWD_1_UNSAFE(zString, iString);
|
||
+
|
||
+ }else if( !prevEscape && uPattern==uEsc){
|
||
+ /* Case 3. */
|
||
+ prevEscape = 1;
|
||
+
|
||
+ }else{
|
||
+ /* Case 4. */
|
||
+ UChar32 uString;
|
||
+ U8_NEXT_UNSAFE(zString, iString, uString);
|
||
+ uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT);
|
||
+ uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT);
|
||
+ if( uString!=uPattern ){
|
||
+ return 0;
|
||
+ }
|
||
+ prevEscape = 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return zString[iString]==0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the like() SQL function. This function implements
|
||
+** the build-in LIKE operator. The first argument to the function is the
|
||
+** pattern and the second argument is the string. So, the SQL statements:
|
||
+**
|
||
+** A LIKE B
|
||
+**
|
||
+** is implemented as like(B, A). If there is an escape character E,
|
||
+**
|
||
+** A LIKE B ESCAPE E
|
||
+**
|
||
+** is mapped to like(B, A, E).
|
||
+*/
|
||
+static void icuLikeFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *zA = sqlite3_value_text(argv[0]);
|
||
+ const unsigned char *zB = sqlite3_value_text(argv[1]);
|
||
+ UChar32 uEsc = 0;
|
||
+
|
||
+ /* Limit the length of the LIKE or GLOB pattern to avoid problems
|
||
+ ** of deep recursion and N*N behavior in patternCompare().
|
||
+ */
|
||
+ if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
|
||
+ sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+
|
||
+ if( argc==3 ){
|
||
+ /* The escape character string must consist of a single UTF-8 character.
|
||
+ ** Otherwise, return an error.
|
||
+ */
|
||
+ int nE= sqlite3_value_bytes(argv[2]);
|
||
+ const unsigned char *zE = sqlite3_value_text(argv[2]);
|
||
+ int i = 0;
|
||
+ if( zE==0 ) return;
|
||
+ U8_NEXT(zE, i, nE, uEsc);
|
||
+ if( i!=nE){
|
||
+ sqlite3_result_error(context,
|
||
+ "ESCAPE expression must be a single character", -1);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if( zA && zB ){
|
||
+ sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** This function is called when an ICU function called from within
|
||
+** the implementation of an SQL scalar function returns an error.
|
||
+**
|
||
+** The scalar function context passed as the first argument is
|
||
+** loaded with an error message based on the following two args.
|
||
+*/
|
||
+static void icuFunctionError(
|
||
+ sqlite3_context *pCtx, /* SQLite scalar function context */
|
||
+ const char *zName, /* Name of ICU function that failed */
|
||
+ UErrorCode e /* Error code returned by ICU function */
|
||
+){
|
||
+ char zBuf[128];
|
||
+ sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
||
+ zBuf[127] = '\0';
|
||
+ sqlite3_result_error(pCtx, zBuf, -1);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Function to delete compiled regexp objects. Registered as
|
||
+** a destructor function with sqlite3_set_auxdata().
|
||
+*/
|
||
+static void icuRegexpDelete(void *p){
|
||
+ URegularExpression *pExpr = (URegularExpression *)p;
|
||
+ uregex_close(pExpr);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of SQLite REGEXP operator. This scalar function takes
|
||
+** two arguments. The first is a regular expression pattern to compile
|
||
+** the second is a string to match against that pattern. If either
|
||
+** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
|
||
+** is 1 if the string matches the pattern, or 0 otherwise.
|
||
+**
|
||
+** SQLite maps the regexp() function to the regexp() operator such
|
||
+** that the following two are equivalent:
|
||
+**
|
||
+** zString REGEXP zPattern
|
||
+** regexp(zPattern, zString)
|
||
+**
|
||
+** Uses the following ICU regexp APIs:
|
||
+**
|
||
+** uregex_open()
|
||
+** uregex_matches()
|
||
+** uregex_close()
|
||
+*/
|
||
+static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||
+ UErrorCode status = U_ZERO_ERROR;
|
||
+ URegularExpression *pExpr;
|
||
+ UBool res;
|
||
+ const UChar *zString = sqlite3_value_text16(apArg[1]);
|
||
+
|
||
+ (void)nArg; /* Unused parameter */
|
||
+
|
||
+ /* If the left hand side of the regexp operator is NULL,
|
||
+ ** then the result is also NULL.
|
||
+ */
|
||
+ if( !zString ){
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ pExpr = sqlite3_get_auxdata(p, 0);
|
||
+ if( !pExpr ){
|
||
+ const UChar *zPattern = sqlite3_value_text16(apArg[0]);
|
||
+ if( !zPattern ){
|
||
+ return;
|
||
+ }
|
||
+ pExpr = uregex_open(zPattern, -1, 0, 0, &status);
|
||
+
|
||
+ if( U_SUCCESS(status) ){
|
||
+ sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
||
+ }else{
|
||
+ assert(!pExpr);
|
||
+ icuFunctionError(p, "uregex_open", status);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Configure the text that the regular expression operates on. */
|
||
+ uregex_setText(pExpr, zString, -1, &status);
|
||
+ if( !U_SUCCESS(status) ){
|
||
+ icuFunctionError(p, "uregex_setText", status);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Attempt the match */
|
||
+ res = uregex_matches(pExpr, 0, &status);
|
||
+ if( !U_SUCCESS(status) ){
|
||
+ icuFunctionError(p, "uregex_matches", status);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* Set the text that the regular expression operates on to a NULL
|
||
+ ** pointer. This is not really necessary, but it is tidier than
|
||
+ ** leaving the regular expression object configured with an invalid
|
||
+ ** pointer after this function returns.
|
||
+ */
|
||
+ uregex_setText(pExpr, 0, 0, &status);
|
||
+
|
||
+ /* Return 1 or 0. */
|
||
+ sqlite3_result_int(p, res ? 1 : 0);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementations of scalar functions for case mapping - upper() and
|
||
+** lower(). Function upper() converts its input to upper-case (ABC).
|
||
+** Function lower() converts to lower-case (abc).
|
||
+**
|
||
+** ICU provides two types of case mapping, "general" case mapping and
|
||
+** "language specific". Refer to ICU documentation for the differences
|
||
+** between the two.
|
||
+**
|
||
+** To utilise "general" case mapping, the upper() or lower() scalar
|
||
+** functions are invoked with one argument:
|
||
+**
|
||
+** upper('ABC') -> 'abc'
|
||
+** lower('abc') -> 'ABC'
|
||
+**
|
||
+** To access ICU "language specific" case mapping, upper() or lower()
|
||
+** should be invoked with two arguments. The second argument is the name
|
||
+** of the locale to use. Passing an empty string ("") or SQL NULL value
|
||
+** as the second argument is the same as invoking the 1 argument version
|
||
+** of upper() or lower().
|
||
+**
|
||
+** lower('I', 'en_us') -> 'i'
|
||
+** lower('I', 'tr_tr') -> 'ı' (small dotless i)
|
||
+**
|
||
+** http://www.icu-project.org/userguide/posix.html#case_mappings
|
||
+*/
|
||
+static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||
+ const UChar *zInput;
|
||
+ UChar *zOutput;
|
||
+ int nInput;
|
||
+ int nOutput;
|
||
+
|
||
+ UErrorCode status = U_ZERO_ERROR;
|
||
+ const char *zLocale = 0;
|
||
+
|
||
+ assert(nArg==1 || nArg==2);
|
||
+ if( nArg==2 ){
|
||
+ zLocale = (const char *)sqlite3_value_text(apArg[1]);
|
||
+ }
|
||
+
|
||
+ zInput = sqlite3_value_text16(apArg[0]);
|
||
+ if( !zInput ){
|
||
+ return;
|
||
+ }
|
||
+ nInput = sqlite3_value_bytes16(apArg[0]);
|
||
+
|
||
+ nOutput = nInput * 2 + 2;
|
||
+ zOutput = sqlite3_malloc(nOutput);
|
||
+ if( !zOutput ){
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ if( sqlite3_user_data(p) ){
|
||
+ u_strToUpper(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
|
||
+ }else{
|
||
+ u_strToLower(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
|
||
+ }
|
||
+
|
||
+ if( !U_SUCCESS(status) ){
|
||
+ icuFunctionError(p, "u_strToLower()/u_strToUpper", status);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ sqlite3_result_text16(p, zOutput, -1, xFree);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Collation sequence destructor function. The pCtx argument points to
|
||
+** a UCollator structure previously allocated using ucol_open().
|
||
+*/
|
||
+static void icuCollationDel(void *pCtx){
|
||
+ UCollator *p = (UCollator *)pCtx;
|
||
+ ucol_close(p);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Collation sequence comparison function. The pCtx argument points to
|
||
+** a UCollator structure previously allocated using ucol_open().
|
||
+*/
|
||
+static int icuCollationColl(
|
||
+ void *pCtx,
|
||
+ int nLeft,
|
||
+ const void *zLeft,
|
||
+ int nRight,
|
||
+ const void *zRight
|
||
+){
|
||
+ UCollationResult res;
|
||
+ UCollator *p = (UCollator *)pCtx;
|
||
+ res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
|
||
+ switch( res ){
|
||
+ case UCOL_LESS: return -1;
|
||
+ case UCOL_GREATER: return +1;
|
||
+ case UCOL_EQUAL: return 0;
|
||
+ }
|
||
+ assert(!"Unexpected return value from ucol_strcoll()");
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the scalar function icu_load_collation().
|
||
+**
|
||
+** This scalar function is used to add ICU collation based collation
|
||
+** types to an SQLite database connection. It is intended to be called
|
||
+** as follows:
|
||
+**
|
||
+** SELECT icu_load_collation(<locale>, <collation-name>);
|
||
+**
|
||
+** Where <locale> is a string containing an ICU locale identifier (i.e.
|
||
+** "en_AU", "tr_TR" etc.) and <collation-name> is the name of the
|
||
+** collation sequence to create.
|
||
+*/
|
||
+static void icuLoadCollation(
|
||
+ sqlite3_context *p,
|
||
+ int nArg,
|
||
+ sqlite3_value **apArg
|
||
+){
|
||
+ sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
|
||
+ UErrorCode status = U_ZERO_ERROR;
|
||
+ const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
|
||
+ const char *zName; /* SQL Collation sequence name (eg. "japanese") */
|
||
+ UCollator *pUCollator; /* ICU library collation object */
|
||
+ int rc; /* Return code from sqlite3_create_collation_x() */
|
||
+
|
||
+ assert(nArg==2);
|
||
+ zLocale = (const char *)sqlite3_value_text(apArg[0]);
|
||
+ zName = (const char *)sqlite3_value_text(apArg[1]);
|
||
+
|
||
+ if( !zLocale || !zName ){
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ pUCollator = ucol_open(zLocale, &status);
|
||
+ if( !U_SUCCESS(status) ){
|
||
+ icuFunctionError(p, "ucol_open", status);
|
||
+ return;
|
||
+ }
|
||
+ assert(p);
|
||
+
|
||
+ rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
|
||
+ icuCollationColl, icuCollationDel
|
||
+ );
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ ucol_close(pUCollator);
|
||
+ sqlite3_result_error(p, "Error registering collation function", -1);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Register the ICU extension functions with database db.
|
||
+*/
|
||
+int sqlite3IcuInit(sqlite3 *db){
|
||
+ struct IcuScalar {
|
||
+ const char *zName; /* Function name */
|
||
+ int nArg; /* Number of arguments */
|
||
+ int enc; /* Optimal text encoding */
|
||
+ void *pContext; /* sqlite3_user_data() context */
|
||
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||
+ } scalars[] = {
|
||
+ {"regexp", 2, SQLITE_UTF8, 0, icuRegexpFunc},
|
||
+
|
||
+ {"lower", 1, SQLITE_UTF16, 0, icuCaseFunc16},
|
||
+ {"lower", 2, SQLITE_UTF16, 0, icuCaseFunc16},
|
||
+ {"upper", 1, SQLITE_UTF16, (void*)1, icuCaseFunc16},
|
||
+ {"upper", 2, SQLITE_UTF16, (void*)1, icuCaseFunc16},
|
||
+
|
||
+ {"lower", 1, SQLITE_UTF8, 0, icuCaseFunc16},
|
||
+ {"lower", 2, SQLITE_UTF8, 0, icuCaseFunc16},
|
||
+ {"upper", 1, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||
+ {"upper", 2, SQLITE_UTF8, (void*)1, icuCaseFunc16},
|
||
+
|
||
+ {"like", 2, SQLITE_UTF8, 0, icuLikeFunc},
|
||
+ {"like", 3, SQLITE_UTF8, 0, icuLikeFunc},
|
||
+
|
||
+ {"icu_load_collation", 2, SQLITE_UTF8, (void*)db, icuLoadCollation},
|
||
+ };
|
||
+
|
||
+ int rc = SQLITE_OK;
|
||
+ int i;
|
||
+
|
||
+ for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
||
+ struct IcuScalar *p = &scalars[i];
|
||
+ rc = sqlite3_create_function(
|
||
+ db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0
|
||
+ );
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+#if !SQLITE_CORE
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_icu_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi)
|
||
+ return sqlite3IcuInit(db);
|
||
+}
|
||
+#endif
|
||
+
|
||
+#endif
|
||
+
|
||
+/************** End of icu.c *************************************************/
|
||
--- origsrc/sqlite-autoconf-3080802/tea/configure.ac 2015-01-30 15:46:11.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/tea/configure.ac 2015-01-31 00:31:56.621156000 +0100
|
||
@@ -79,6 +79,9 @@ TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
|
||
TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
|
||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
|
||
TEA_ADD_CFLAGS([-DSQLITE_OMIT_DEPRECATED=1])
|
||
+TEA_ADD_CFLAGS([-DSQLITE_MAX_PATH_LENGTH=4096])
|
||
+TEA_ADD_CFLAGS([-DSQLITE_WIN32_NO_ANSI=1])
|
||
+TEA_ADD_CFLAGS([-DSQLITE_WIN32_GETVERSIONEX=0])
|
||
TEA_ADD_STUB_SOURCES([])
|
||
TEA_ADD_TCL_SOURCES([])
|
||
|
||
@@ -168,8 +171,9 @@ AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stu
|
||
#--------------------------------------------------------------------
|
||
#
|
||
#AC_CHECK_FUNC(fdatasync, , AC_DEFINE(fdatasync, fsync))
|
||
+AC_CHECK_HEADERS([malloc.h])
|
||
# Check for library functions that SQLite can optionally use.
|
||
-AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
|
||
+AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usable_size strchrnul usleep utime flock])
|
||
|
||
AC_FUNC_STRERROR_R
|
||
|
||
--- origsrc/sqlite-autoconf-3080802/tea/generic/tclsqlite3.c 2015-01-30 15:46:11.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/tea/generic/tclsqlite3.c 2015-01-31 00:31:56.632156700 +0100
|
||
@@ -1,8 +1,3 @@
|
||
-#ifdef USE_SYSTEM_SQLITE
|
||
-# include <sqlite3.h>
|
||
-#else
|
||
-#include "sqlite3.c"
|
||
-#endif
|
||
/*
|
||
** 2001 September 15
|
||
**
|
||
@@ -182,6 +177,10 @@ static int strlen30(const char *z){
|
||
return 0x3fffffff & (int)(z2 - z);
|
||
}
|
||
|
||
+#ifdef USE_TCL_STUBS
|
||
+# define tclStubsPtr staticTclStubsPtr
|
||
+static const TclStubs *tclStubsPtr = NULL;
|
||
+#endif
|
||
|
||
#ifndef SQLITE_OMIT_INCRBLOB
|
||
/*
|
||
@@ -269,7 +268,7 @@ static int incrblobInput(
|
||
*/
|
||
static int incrblobOutput(
|
||
ClientData instanceData,
|
||
- CONST char *buf,
|
||
+ const char *buf,
|
||
int toWrite,
|
||
int *errorCodePtr
|
||
){
|
||
@@ -666,9 +665,9 @@ static int DbWalHandler(
|
||
#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
|
||
static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){
|
||
char zBuf[64];
|
||
- sprintf(zBuf, "%d", iArg);
|
||
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iArg);
|
||
Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY);
|
||
- sprintf(zBuf, "%d", nArg);
|
||
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nArg);
|
||
Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY);
|
||
}
|
||
#else
|
||
@@ -3089,9 +3088,21 @@ static int DbMain(void *cd, Tcl_Interp *
|
||
** Provide a dummy Tcl_InitStubs if we are using this as a static
|
||
** library.
|
||
*/
|
||
+#undef Tcl_InitStubs
|
||
#ifndef USE_TCL_STUBS
|
||
-# undef Tcl_InitStubs
|
||
# define Tcl_InitStubs(a,b,c) TCL_VERSION
|
||
+#else
|
||
+# define Tcl_InitStubs staticTclInitStubs
|
||
+typedef struct {
|
||
+ char *result;
|
||
+ Tcl_FreeProc *freeProc;
|
||
+ int errorLine;
|
||
+ const struct TclStubs *stubTable;
|
||
+} PrivateTclInterp;
|
||
+static const char *Tcl_InitStubs(Tcl_Interp *interp, const char *version, int exact) {
|
||
+ tclStubsPtr = ((PrivateTclInterp *)interp)->stubTable;
|
||
+ return Tcl_PkgRequireEx(interp, "Tcl", version, 0, (void *)&tclStubsPtr);
|
||
+}
|
||
#endif
|
||
|
||
/*
|
||
@@ -3112,9 +3123,9 @@ static int DbMain(void *cd, Tcl_Interp *
|
||
** used to open a new SQLite database. See the DbMain() routine above
|
||
** for additional information.
|
||
**
|
||
-** The EXTERN macros are required by TCL in order to work on windows.
|
||
+** The DLLEXPORT macros are required by TCL in order to work on windows.
|
||
*/
|
||
-EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
||
+DLLEXPORT int Sqlite3_Init(Tcl_Interp *interp){
|
||
int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR;
|
||
if( rc==TCL_OK ){
|
||
Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
|
||
@@ -3128,9 +3139,9 @@ EXTERN int Sqlite3_Init(Tcl_Interp *inte
|
||
}
|
||
return rc;
|
||
}
|
||
-EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||
-EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||
-EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||
+DLLEXPORT int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||
+DLLEXPORT int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||
+DLLEXPORT int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||
|
||
/* Because it accesses the file-system and uses persistent state, SQLite
|
||
** is not considered appropriate for safe interpreters. Hence, we deliberately
|
||
@@ -3434,7 +3445,7 @@ static void MD5DigestToBase10x8(unsigned
|
||
for(i=j=0; i<16; i+=2){
|
||
x = digest[i]*256 + digest[i+1];
|
||
if( i>0 ) zDigest[j++] = '-';
|
||
- sprintf(&zDigest[j], "%05u", x);
|
||
+ sqlite3_snprintf(50-j, &zDigest[j], "%05u", x);
|
||
j += 5;
|
||
}
|
||
zDigest[j] = 0;
|
||
@@ -3600,7 +3611,7 @@ static int init_all_cmd(
|
||
ClientData cd,
|
||
Tcl_Interp *interp,
|
||
int objc,
|
||
- Tcl_Obj *CONST objv[]
|
||
+ Tcl_Obj *const objv[]
|
||
){
|
||
|
||
Tcl_Interp *slave;
|
||
@@ -3630,7 +3641,7 @@ static int db_use_legacy_prepare_cmd(
|
||
ClientData cd,
|
||
Tcl_Interp *interp,
|
||
int objc,
|
||
- Tcl_Obj *CONST objv[]
|
||
+ Tcl_Obj *const objv[]
|
||
){
|
||
Tcl_CmdInfo cmdInfo;
|
||
SqliteDb *pDb;
|
||
@@ -3667,7 +3678,7 @@ static int db_last_stmt_ptr(
|
||
ClientData cd,
|
||
Tcl_Interp *interp,
|
||
int objc,
|
||
- Tcl_Obj *CONST objv[]
|
||
+ Tcl_Obj *const objv[]
|
||
){
|
||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
||
Tcl_CmdInfo cmdInfo;
|
||
--- origsrc/sqlite-autoconf-3080802/tea/tclconfig/tcl.m4 2015-01-30 15:46:09.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/tea/tclconfig/tcl.m4 2015-01-31 00:31:56.643157300 +0100
|
||
@@ -1344,7 +1344,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
||
fi
|
||
|
||
SHLIB_SUFFIX=".dll"
|
||
- SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll'
|
||
+ SHARED_LIB_SUFFIX='3.dll'
|
||
|
||
TCL_LIB_VERSIONS_OK=nodots
|
||
;;
|
||
@@ -1429,6 +1429,7 @@ AC_DEFUN([TEA_CONFIG_CFLAGS], [
|
||
SHLIB_SUFFIX=".dll"
|
||
EXEEXT=".exe"
|
||
do64bit_ok=yes
|
||
+ SHARED_LIB_SUFFIX='3.dll'
|
||
CC_SEARCH_FLAGS=""
|
||
LD_SEARCH_FLAGS=""
|
||
;;
|
||
@@ -3357,7 +3358,7 @@ print("manifest needed")
|
||
if test "$GCC" = "yes"; then
|
||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc"
|
||
fi
|
||
- eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
||
+ eval eval "PKG_LIB_FILE=tcl${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
||
else
|
||
eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
||
if test "$GCC" = "yes"; then
|
||
@@ -3379,7 +3380,7 @@ print("manifest needed")
|
||
if test x"${TK_BIN_DIR}" != x ; then
|
||
SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
|
||
fi
|
||
- eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
||
+ eval eval "PKG_LIB_FILE=tcl${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
|
||
RANLIB=:
|
||
else
|
||
eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
|
||
--- origsrc/sqlite-autoconf-3080802/totype.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/totype.c 2015-01-31 00:31:56.655158000 +0100
|
||
@@ -0,0 +1,530 @@
|
||
+/*
|
||
+** 2013-10-14
|
||
+**
|
||
+** 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 SQLite extension implements functions tointeger(X) and toreal(X).
|
||
+**
|
||
+** If X is an integer, real, or string value that can be
|
||
+** losslessly represented as an integer, then tointeger(X)
|
||
+** returns the corresponding integer value.
|
||
+** If X is an 8-byte BLOB then that blob is interpreted as
|
||
+** a signed two-compliment little-endian encoding of an integer
|
||
+** and tointeger(X) returns the corresponding integer value.
|
||
+** Otherwise tointeger(X) return NULL.
|
||
+**
|
||
+** If X is an integer, real, or string value that can be
|
||
+** convert into a real number, preserving at least 15 digits
|
||
+** of precision, then toreal(X) returns the corresponding real value.
|
||
+** If X is an 8-byte BLOB then that blob is interpreted as
|
||
+** a 64-bit IEEE754 big-endian floating point value
|
||
+** and toreal(X) returns the corresponding real value.
|
||
+** Otherwise toreal(X) return NULL.
|
||
+**
|
||
+** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
|
||
+** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
|
||
+** encoding.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+
|
||
+/*
|
||
+** Determine if this is running on a big-endian or little-endian
|
||
+** processor
|
||
+*/
|
||
+#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
|
||
+ || defined(__x86_64) || defined(__x86_64__)
|
||
+# define TOTYPE_BIGENDIAN 0
|
||
+# define TOTYPE_LITTLEENDIAN 1
|
||
+#else
|
||
+ const int totype_one = 1;
|
||
+# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
|
||
+# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Constants for the largest and smallest possible 64-bit signed integers.
|
||
+** These macros are designed to work correctly on both 32-bit and 64-bit
|
||
+** compilers.
|
||
+*/
|
||
+#ifndef LARGEST_INT64
|
||
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||
+#endif
|
||
+
|
||
+#ifndef SMALLEST_INT64
|
||
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Return TRUE if character c is a whitespace character
|
||
+*/
|
||
+static int totypeIsspace(unsigned char c){
|
||
+ return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return TRUE if character c is a digit
|
||
+*/
|
||
+static int totypeIsdigit(unsigned char c){
|
||
+ return c>='0' && c<='9';
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compare the 19-character string zNum against the text representation
|
||
+** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
||
+** if zNum is less than, equal to, or greater than the string.
|
||
+** Note that zNum must contain exactly 19 characters.
|
||
+**
|
||
+** Unlike memcmp() this routine is guaranteed to return the difference
|
||
+** in the values of the last digit if the only difference is in the
|
||
+** last digit. So, for example,
|
||
+**
|
||
+** totypeCompare2pow63("9223372036854775800")
|
||
+**
|
||
+** will return -8.
|
||
+*/
|
||
+static int totypeCompare2pow63(const char *zNum){
|
||
+ int c = 0;
|
||
+ int i;
|
||
+ /* 012345678901234567 */
|
||
+ const char *pow63 = "922337203685477580";
|
||
+ for(i=0; c==0 && i<18; i++){
|
||
+ c = (zNum[i]-pow63[i])*10;
|
||
+ }
|
||
+ if( c==0 ){
|
||
+ c = zNum[18] - '8';
|
||
+ }
|
||
+ return c;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Convert zNum to a 64-bit signed integer.
|
||
+**
|
||
+** If the zNum value is representable as a 64-bit twos-complement
|
||
+** integer, then write that value into *pNum and return 0.
|
||
+**
|
||
+** If zNum is exactly 9223372036854665808, return 2. This special
|
||
+** case is broken out because while 9223372036854665808 cannot be a
|
||
+** signed 64-bit integer, its negative -9223372036854665808 can be.
|
||
+**
|
||
+** If zNum is too big for a 64-bit integer and is not
|
||
+** 9223372036854665808 or if zNum contains any non-numeric text,
|
||
+** then return 1.
|
||
+**
|
||
+** The string is not necessarily zero-terminated.
|
||
+*/
|
||
+static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
|
||
+ sqlite3_uint64 u = 0;
|
||
+ int neg = 0; /* assume positive */
|
||
+ int i;
|
||
+ int c = 0;
|
||
+ int nonNum = 0;
|
||
+ const char *zStart;
|
||
+ const char *zEnd = zNum + length;
|
||
+
|
||
+ while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
|
||
+ if( zNum<zEnd ){
|
||
+ if( *zNum=='-' ){
|
||
+ neg = 1;
|
||
+ zNum++;
|
||
+ }else if( *zNum=='+' ){
|
||
+ zNum++;
|
||
+ }
|
||
+ }
|
||
+ zStart = zNum;
|
||
+ while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
|
||
+ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
|
||
+ u = u*10 + c - '0';
|
||
+ }
|
||
+ if( u>LARGEST_INT64 ){
|
||
+ *pNum = SMALLEST_INT64;
|
||
+ }else if( neg ){
|
||
+ *pNum = -(sqlite3_int64)u;
|
||
+ }else{
|
||
+ *pNum = (sqlite3_int64)u;
|
||
+ }
|
||
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
|
||
+ /* zNum is empty or contains non-numeric text or is longer
|
||
+ ** than 19 digits (thus guaranteeing that it is too large) */
|
||
+ return 1;
|
||
+ }else if( i<19 ){
|
||
+ /* Less than 19 digits, so we know that it fits in 64 bits */
|
||
+ assert( u<=LARGEST_INT64 );
|
||
+ return 0;
|
||
+ }else{
|
||
+ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
||
+ c = totypeCompare2pow63(zNum);
|
||
+ if( c<0 ){
|
||
+ /* zNum is less than 9223372036854775808 so it fits */
|
||
+ assert( u<=LARGEST_INT64 );
|
||
+ return 0;
|
||
+ }else if( c>0 ){
|
||
+ /* zNum is greater than 9223372036854775808 so it overflows */
|
||
+ return 1;
|
||
+ }else{
|
||
+ /* zNum is exactly 9223372036854775808. Fits if negative. The
|
||
+ ** special case 2 overflow if positive */
|
||
+ assert( u-1==LARGEST_INT64 );
|
||
+ assert( (*pNum)==SMALLEST_INT64 );
|
||
+ return neg ? 0 : 2;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** The string z[] is an text representation of a real number.
|
||
+** Convert this string to a double and write it into *pResult.
|
||
+**
|
||
+** The string is not necessarily zero-terminated.
|
||
+**
|
||
+** Return TRUE if the result is a valid real number (or integer) and FALSE
|
||
+** if the string is empty or contains extraneous text. Valid numbers
|
||
+** are in one of these formats:
|
||
+**
|
||
+** [+-]digits[E[+-]digits]
|
||
+** [+-]digits.[digits][E[+-]digits]
|
||
+** [+-].digits[E[+-]digits]
|
||
+**
|
||
+** Leading and trailing whitespace is ignored for the purpose of determining
|
||
+** validity.
|
||
+**
|
||
+** If some prefix of the input string is a valid number, this routine
|
||
+** returns FALSE but it still converts the prefix and writes the result
|
||
+** into *pResult.
|
||
+*/
|
||
+static int totypeAtoF(const char *z, double *pResult, int length){
|
||
+ const char *zEnd = z + length;
|
||
+ /* sign * significand * (10 ^ (esign * exponent)) */
|
||
+ int sign = 1; /* sign of significand */
|
||
+ sqlite3_int64 s = 0; /* significand */
|
||
+ int d = 0; /* adjust exponent for shifting decimal point */
|
||
+ int esign = 1; /* sign of exponent */
|
||
+ int e = 0; /* exponent */
|
||
+ int eValid = 1; /* True exponent is either not used or is well-formed */
|
||
+ double result;
|
||
+ int nDigits = 0;
|
||
+ int nonNum = 0;
|
||
+
|
||
+ *pResult = 0.0; /* Default return value, in case of an error */
|
||
+
|
||
+ /* skip leading spaces */
|
||
+ while( z<zEnd && totypeIsspace(*z) ) z++;
|
||
+ if( z>=zEnd ) return 0;
|
||
+
|
||
+ /* get sign of significand */
|
||
+ if( *z=='-' ){
|
||
+ sign = -1;
|
||
+ z++;
|
||
+ }else if( *z=='+' ){
|
||
+ z++;
|
||
+ }
|
||
+
|
||
+ /* skip leading zeroes */
|
||
+ while( z<zEnd && z[0]=='0' ) z++, nDigits++;
|
||
+
|
||
+ /* copy max significant digits to significand */
|
||
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||
+ s = s*10 + (*z - '0');
|
||
+ z++, nDigits++;
|
||
+ }
|
||
+
|
||
+ /* skip non-significant significand digits
|
||
+ ** (increase exponent by d to shift decimal left) */
|
||
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
|
||
+ if( z>=zEnd ) goto totype_atof_calc;
|
||
+
|
||
+ /* if decimal point is present */
|
||
+ if( *z=='.' ){
|
||
+ z++;
|
||
+ /* copy digits from after decimal to significand
|
||
+ ** (decrease exponent by d to shift decimal right) */
|
||
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||
+ s = s*10 + (*z - '0');
|
||
+ z++, nDigits++, d--;
|
||
+ }
|
||
+ /* skip non-significant digits */
|
||
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
|
||
+ }
|
||
+ if( z>=zEnd ) goto totype_atof_calc;
|
||
+
|
||
+ /* if exponent is present */
|
||
+ if( *z=='e' || *z=='E' ){
|
||
+ z++;
|
||
+ eValid = 0;
|
||
+ if( z>=zEnd ) goto totype_atof_calc;
|
||
+ /* get sign of exponent */
|
||
+ if( *z=='-' ){
|
||
+ esign = -1;
|
||
+ z++;
|
||
+ }else if( *z=='+' ){
|
||
+ z++;
|
||
+ }
|
||
+ /* copy digits to exponent */
|
||
+ while( z<zEnd && totypeIsdigit(*z) ){
|
||
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
||
+ z++;
|
||
+ eValid = 1;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* skip trailing spaces */
|
||
+ if( nDigits && eValid ){
|
||
+ while( z<zEnd && totypeIsspace(*z) ) z++;
|
||
+ }
|
||
+
|
||
+totype_atof_calc:
|
||
+ /* adjust exponent by d, and update sign */
|
||
+ e = (e*esign) + d;
|
||
+ if( e<0 ) {
|
||
+ esign = -1;
|
||
+ e *= -1;
|
||
+ } else {
|
||
+ esign = 1;
|
||
+ }
|
||
+
|
||
+ /* if 0 significand */
|
||
+ if( !s ) {
|
||
+ /* In the IEEE 754 standard, zero is signed.
|
||
+ ** Add the sign if we've seen at least one digit */
|
||
+ result = (sign<0 && nDigits) ? -(double)0 : (double)0;
|
||
+ } else {
|
||
+ /* attempt to reduce exponent */
|
||
+ if( esign>0 ){
|
||
+ while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
|
||
+ }else{
|
||
+ while( !(s%10) && e>0 ) e--,s/=10;
|
||
+ }
|
||
+
|
||
+ /* adjust the sign of significand */
|
||
+ s = sign<0 ? -s : s;
|
||
+
|
||
+ /* if exponent, scale significand as appropriate
|
||
+ ** and store in result. */
|
||
+ if( e ){
|
||
+ double scale = 1.0;
|
||
+ /* attempt to handle extremely small/large numbers better */
|
||
+ if( e>307 && e<342 ){
|
||
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
||
+ if( esign<0 ){
|
||
+ result = s / scale;
|
||
+ result /= 1.0e+308;
|
||
+ }else{
|
||
+ result = s * scale;
|
||
+ result *= 1.0e+308;
|
||
+ }
|
||
+ }else if( e>=342 ){
|
||
+ if( esign<0 ){
|
||
+ result = 0.0*s;
|
||
+ }else{
|
||
+ result = 1e308*1e308*s; /* Infinity */
|
||
+ }
|
||
+ }else{
|
||
+ /* 1.0e+22 is the largest power of 10 than can be
|
||
+ ** represented exactly. */
|
||
+ while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
||
+ while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
||
+ if( esign<0 ){
|
||
+ result = s / scale;
|
||
+ }else{
|
||
+ result = s * scale;
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ result = (double)s;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* store the result */
|
||
+ *pResult = result;
|
||
+
|
||
+ /* return true if number and no extra non-whitespace chracters after */
|
||
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** tointeger(X): If X is any value (integer, double, blob, or string) that
|
||
+** can be losslessly converted into an integer, then make the conversion and
|
||
+** return the result. Otherwise, return NULL.
|
||
+*/
|
||
+static void tointegerFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ assert( argc==1 );
|
||
+ (void)argc;
|
||
+ switch( sqlite3_value_type(argv[0]) ){
|
||
+ case SQLITE_FLOAT: {
|
||
+ double rVal = sqlite3_value_double(argv[0]);
|
||
+ sqlite3_int64 iVal = (sqlite3_int64)rVal;
|
||
+ if( rVal==(double)iVal ){
|
||
+ sqlite3_result_int64(context, iVal);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_INTEGER: {
|
||
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_BLOB: {
|
||
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||
+ if( zBlob ){
|
||
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
||
+ if( nBlob==sizeof(sqlite3_int64) ){
|
||
+ sqlite3_int64 iVal;
|
||
+ if( TOTYPE_BIGENDIAN ){
|
||
+ int i;
|
||
+ unsigned char zBlobRev[sizeof(sqlite3_int64)];
|
||
+ for(i=0; i<sizeof(sqlite3_int64); i++){
|
||
+ zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
|
||
+ }
|
||
+ memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
|
||
+ }else{
|
||
+ memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
|
||
+ }
|
||
+ sqlite3_result_int64(context, iVal);
|
||
+ }
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_TEXT: {
|
||
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||
+ if( zStr ){
|
||
+ int nStr = sqlite3_value_bytes(argv[0]);
|
||
+ if( nStr && !totypeIsspace(zStr[0]) ){
|
||
+ sqlite3_int64 iVal;
|
||
+ if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
|
||
+ sqlite3_result_int64(context, iVal);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ default: {
|
||
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** toreal(X): If X is any value (integer, double, blob, or string) that can
|
||
+** be losslessly converted into a real number, then do so and return that
|
||
+** real number. Otherwise return NULL.
|
||
+*/
|
||
+#if defined(_MSC_VER)
|
||
+#pragma warning(disable: 4748)
|
||
+#pragma optimize("", off)
|
||
+#endif
|
||
+static void torealFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ assert( argc==1 );
|
||
+ (void)argc;
|
||
+ switch( sqlite3_value_type(argv[0]) ){
|
||
+ case SQLITE_FLOAT: {
|
||
+ sqlite3_result_double(context, sqlite3_value_double(argv[0]));
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_INTEGER: {
|
||
+ sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
|
||
+ double rVal = (double)iVal;
|
||
+ if( iVal==(sqlite3_int64)rVal ){
|
||
+ sqlite3_result_double(context, rVal);
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_BLOB: {
|
||
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||
+ if( zBlob ){
|
||
+ int nBlob = sqlite3_value_bytes(argv[0]);
|
||
+ if( nBlob==sizeof(double) ){
|
||
+ double rVal;
|
||
+ if( TOTYPE_LITTLEENDIAN ){
|
||
+ int i;
|
||
+ unsigned char zBlobRev[sizeof(double)];
|
||
+ for(i=0; i<sizeof(double); i++){
|
||
+ zBlobRev[i] = zBlob[sizeof(double)-1-i];
|
||
+ }
|
||
+ memcpy(&rVal, zBlobRev, sizeof(double));
|
||
+ }else{
|
||
+ memcpy(&rVal, zBlob, sizeof(double));
|
||
+ }
|
||
+ sqlite3_result_double(context, rVal);
|
||
+ }
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case SQLITE_TEXT: {
|
||
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||
+ if( zStr ){
|
||
+ int nStr = sqlite3_value_bytes(argv[0]);
|
||
+ if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
|
||
+ double rVal;
|
||
+ if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
|
||
+ sqlite3_result_double(context, rVal);
|
||
+ return;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ default: {
|
||
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+#if defined(_MSC_VER)
|
||
+#pragma optimize("", on)
|
||
+#pragma warning(default: 4748)
|
||
+#endif
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_totype_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
||
+ tointegerFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
||
+ torealFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ (void)pzErrMsg; /* Unused parameter */
|
||
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
|
||
+ tointegerFunc, 0, 0);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
|
||
+ torealFunc, 0, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/vfslog.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/vfslog.c 2015-01-31 00:31:56.669158800 +0100
|
||
@@ -0,0 +1,852 @@
|
||
+/*
|
||
+** 2013-10-09
|
||
+**
|
||
+** 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 contains the implementation of an SQLite vfs wrapper for
|
||
+** unix that generates per-database log files of all disk activity.
|
||
+*/
|
||
+
|
||
+/*
|
||
+** This module contains code for a wrapper VFS that causes a log of
|
||
+** most VFS calls to be written into a file on disk.
|
||
+**
|
||
+** Each database connection creates a separate log file in the same
|
||
+** directory as the original database and named after the original
|
||
+** database. A unique suffix is added to avoid name collisions.
|
||
+** Separate log files are used so that concurrent processes do not
|
||
+** try to write log operations to the same file at the same instant,
|
||
+** resulting in overwritten or comingled log text.
|
||
+**
|
||
+** Each individual log file records operations by a single database
|
||
+** connection on both the original database and its associated rollback
|
||
+** journal.
|
||
+**
|
||
+** The log files are in the comma-separated-value (CSV) format. The
|
||
+** log files can be imported into an SQLite database using the ".import"
|
||
+** command of the SQLite command-line shell for analysis.
|
||
+**
|
||
+** One technique for using this module is to append the text of this
|
||
+** module to the end of a standard "sqlite3.c" amalgamation file then
|
||
+** add the following compile-time options:
|
||
+**
|
||
+** -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog
|
||
+** -DSQLITE_USE_FCNTL_TRACE
|
||
+**
|
||
+** The first compile-time option causes the sqlite3_register_vfslog()
|
||
+** function, defined below, to be invoked when SQLite is initialized.
|
||
+** That causes this custom VFS to become the default VFS for all
|
||
+** subsequent connections. The SQLITE_USE_FCNTL_TRACE option causes
|
||
+** the SQLite core to issue extra sqlite3_file_control() operations
|
||
+** with SQLITE_FCNTL_TRACE to give some indication of what is going
|
||
+** on in the core.
|
||
+*/
|
||
+
|
||
+#include "sqlite3.h"
|
||
+#include <string.h>
|
||
+#include <assert.h>
|
||
+#include <stdio.h>
|
||
+#if SQLITE_OS_UNIX
|
||
+# include <unistd.h>
|
||
+#endif
|
||
+
|
||
+/*
|
||
+** Forward declaration of objects used by this utility
|
||
+*/
|
||
+typedef struct VLogLog VLogLog;
|
||
+typedef struct VLogVfs VLogVfs;
|
||
+typedef struct VLogFile VLogFile;
|
||
+
|
||
+/* There is a pair (an array of size 2) of the following objects for
|
||
+** each database file being logged. The first contains the filename
|
||
+** and is used to log I/O with the main database. The second has
|
||
+** a NULL filename and is used to log I/O for the journal. Both
|
||
+** out pointers are the same.
|
||
+*/
|
||
+struct VLogLog {
|
||
+ VLogLog *pNext; /* Next in a list of all active logs */
|
||
+ VLogLog **ppPrev; /* Pointer to this in the list */
|
||
+ int nRef; /* Number of references to this object */
|
||
+ int nFilename; /* Length of zFilename in bytes */
|
||
+ char *zFilename; /* Name of database file. NULL for journal */
|
||
+ FILE *out; /* Write information here */
|
||
+};
|
||
+
|
||
+struct VLogVfs {
|
||
+ sqlite3_vfs base; /* VFS methods */
|
||
+ sqlite3_vfs *pVfs; /* Parent VFS */
|
||
+};
|
||
+
|
||
+struct VLogFile {
|
||
+ sqlite3_file base; /* IO methods */
|
||
+ sqlite3_file *pReal; /* Underlying file handle */
|
||
+ VLogLog *pLog; /* The log file for this file */
|
||
+};
|
||
+
|
||
+#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
|
||
+
|
||
+/*
|
||
+** Methods for VLogFile
|
||
+*/
|
||
+static int vlogClose(sqlite3_file*);
|
||
+static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||
+static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||
+static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
|
||
+static int vlogSync(sqlite3_file*, int flags);
|
||
+static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||
+static int vlogLock(sqlite3_file*, int);
|
||
+static int vlogUnlock(sqlite3_file*, int);
|
||
+static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
|
||
+static int vlogFileControl(sqlite3_file*, int op, void *pArg);
|
||
+static int vlogSectorSize(sqlite3_file*);
|
||
+static int vlogDeviceCharacteristics(sqlite3_file*);
|
||
+static int vlogShmMap(sqlite3_file*,int,int,int, void volatile **);
|
||
+static int vlogShmLock(sqlite3_file*, int , int, int);
|
||
+static void vlogShmBarrier(sqlite3_file*);
|
||
+static int vlogShmUnmap(sqlite3_file*, int);
|
||
+static int vlogFetch(sqlite3_file*, sqlite3_int64, int, void**);
|
||
+static int vlogUnfetch(sqlite3_file*, sqlite3_int64, void*);
|
||
+
|
||
+/*
|
||
+** Methods for VLogVfs
|
||
+*/
|
||
+static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||
+static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||
+static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||
+static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||
+static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
|
||
+static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||
+static void vlogDlClose(sqlite3_vfs*, void*);
|
||
+static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||
+static int vlogSleep(sqlite3_vfs*, int microseconds);
|
||
+static int vlogCurrentTime(sqlite3_vfs*, double*);
|
||
+static int vlogGetLastError(sqlite3_vfs*, int, char *);
|
||
+static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||
+static int vlogSetSystemCall(sqlite3_vfs*, const char *, sqlite3_syscall_ptr);
|
||
+static sqlite3_syscall_ptr vlogGetSystemCall(sqlite3_vfs *, const char *);
|
||
+static const char *vlogNextSystemCall(sqlite3_vfs *, const char *);
|
||
+
|
||
+
|
||
+static VLogVfs vlog_vfs = {
|
||
+ {
|
||
+ 0, /* iVersion (set by register_vlog()) */
|
||
+ 0, /* szOsFile (set by register_vlog()) */
|
||
+ 0, /* mxPathname (set by register_vlog()) */
|
||
+ 0, /* pNext */
|
||
+ "vfslog", /* zName */
|
||
+ 0, /* pAppData */
|
||
+ vlogOpen, /* xOpen */
|
||
+ vlogDelete, /* xDelete */
|
||
+ vlogAccess, /* xAccess */
|
||
+ vlogFullPathname, /* xFullPathname */
|
||
+ vlogDlOpen, /* xDlOpen */
|
||
+ vlogDlError, /* xDlError */
|
||
+ vlogDlSym, /* xDlSym */
|
||
+ vlogDlClose, /* xDlClose */
|
||
+ vlogRandomness, /* xRandomness */
|
||
+ vlogSleep, /* xSleep */
|
||
+ vlogCurrentTime, /* xCurrentTime */
|
||
+ vlogGetLastError, /* xGetLastError */
|
||
+ vlogCurrentTimeInt64, /* xCurrentTimeInt64 */
|
||
+ vlogSetSystemCall, /* xSetSystemCall */
|
||
+ vlogGetSystemCall, /* xGetSystemCall */
|
||
+ vlogNextSystemCall /* xNextSystemCall */
|
||
+ },
|
||
+ 0
|
||
+};
|
||
+
|
||
+static sqlite3_io_methods vlog_io_methods = {
|
||
+ 3, /* iVersion */
|
||
+ vlogClose, /* xClose */
|
||
+ vlogRead, /* xRead */
|
||
+ vlogWrite, /* xWrite */
|
||
+ vlogTruncate, /* xTruncate */
|
||
+ vlogSync, /* xSync */
|
||
+ vlogFileSize, /* xFileSize */
|
||
+ vlogLock, /* xLock */
|
||
+ vlogUnlock, /* xUnlock */
|
||
+ vlogCheckReservedLock, /* xCheckReservedLock */
|
||
+ vlogFileControl, /* xFileControl */
|
||
+ vlogSectorSize, /* xSectorSize */
|
||
+ vlogDeviceCharacteristics, /* xDeviceCharacteristics */
|
||
+ vlogShmMap, /* xShmMap */
|
||
+ vlogShmLock, /* xShmLock */
|
||
+ vlogShmBarrier, /* xShmBarrier */
|
||
+ vlogShmUnmap, /* xShmUnmap */
|
||
+ vlogFetch, /* xFetch */
|
||
+ vlogUnfetch /* xUnfecth */
|
||
+};
|
||
+
|
||
+#ifdef _WIN32
|
||
+#include <windows.h>
|
||
+#include <time.h>
|
||
+static sqlite3_uint64 vlog_time(){
|
||
+ FILETIME ft;
|
||
+ sqlite3_uint64 u64time = 0;
|
||
+
|
||
+ GetSystemTimeAsFileTime(&ft);
|
||
+
|
||
+ u64time |= ft.dwHighDateTime;
|
||
+ u64time <<= 32;
|
||
+ u64time |= ft.dwLowDateTime;
|
||
+
|
||
+ /* ft is 100-nanosecond intervals, we want microseconds */
|
||
+ return u64time /(sqlite3_uint64)10;
|
||
+}
|
||
+#elif SQLITE_OS_UNIX && !defined(NO_GETTOD)
|
||
+#include <sys/time.h>
|
||
+static sqlite3_uint64 vlog_time(){
|
||
+ struct timeval sTime;
|
||
+ gettimeofday(&sTime, 0);
|
||
+ return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
|
||
+}
|
||
+#else
|
||
+static sqlite3_uint64 vlog_time(){
|
||
+ return 0;
|
||
+}
|
||
+#endif
|
||
+
|
||
+
|
||
+/*
|
||
+** Write a message to the log file
|
||
+*/
|
||
+static void vlogLogPrint(
|
||
+ VLogLog *pLog, /* The log file to write into */
|
||
+ sqlite3_int64 tStart, /* Start time of system call */
|
||
+ sqlite3_int64 tElapse, /* Elapse time of system call */
|
||
+ const char *zOp, /* Type of system call */
|
||
+ sqlite3_int64 iArg1, /* First argument */
|
||
+ sqlite3_int64 iArg2, /* Second argument */
|
||
+ const char *zArg3, /* Third argument */
|
||
+ int iRes /* Result */
|
||
+){
|
||
+ char z1[40], z2[40], z3[2000];
|
||
+ if( pLog==0 ) return;
|
||
+ if( iArg1>=0 ){
|
||
+ sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
|
||
+ }else{
|
||
+ z1[0] = 0;
|
||
+ }
|
||
+ if( iArg2>=0 ){
|
||
+ sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
|
||
+ }else{
|
||
+ z2[0] = 0;
|
||
+ }
|
||
+ if( zArg3 ){
|
||
+ sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
|
||
+ }else{
|
||
+ z3[0] = 0;
|
||
+ }
|
||
+ fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
|
||
+ tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
|
||
+}
|
||
+
|
||
+/*
|
||
+** List of all active log connections. Protected by the master mutex.
|
||
+*/
|
||
+static VLogLog *allLogs = 0;
|
||
+
|
||
+/*
|
||
+** Close a VLogLog object
|
||
+*/
|
||
+static void vlogLogClose(VLogLog *p){
|
||
+ if( p ){
|
||
+ sqlite3_mutex *pMutex;
|
||
+ p->nRef--;
|
||
+ if( p->nRef>0 || p->zFilename==0 ) return;
|
||
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
||
+ sqlite3_mutex_enter(pMutex);
|
||
+ *p->ppPrev = p->pNext;
|
||
+ if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
|
||
+ sqlite3_mutex_leave(pMutex);
|
||
+ fclose(p->out);
|
||
+ sqlite3_free(p);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open a VLogLog object on the given file
|
||
+*/
|
||
+static VLogLog *vlogLogOpen(const char *zFilename){
|
||
+ int nName = (int)strlen(zFilename);
|
||
+ int isJournal = 0;
|
||
+ sqlite3_mutex *pMutex;
|
||
+ VLogLog *pLog, *pTemp;
|
||
+ sqlite3_int64 tNow = 0;
|
||
+ if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
|
||
+ return 0; /* Do not log wal files */
|
||
+ }else
|
||
+ if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
|
||
+ nName -= 8;
|
||
+ isJournal = 1;
|
||
+ }else if( nName>12
|
||
+ && sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
|
||
+ return 0; /* Do not log master journal files */
|
||
+ }
|
||
+ pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
|
||
+ if( pTemp==0 ) return 0;
|
||
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
|
||
+ sqlite3_mutex_enter(pMutex);
|
||
+ for(pLog=allLogs; pLog; pLog=pLog->pNext){
|
||
+ if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if( pLog==0 ){
|
||
+ pLog = pTemp;
|
||
+ pTemp = 0;
|
||
+ memset(pLog, 0, sizeof(*pLog)*2);
|
||
+ pLog->zFilename = (char*)&pLog[2];
|
||
+ tNow = vlog_time();
|
||
+ sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
|
||
+ nName, zFilename, tNow);
|
||
+ pLog->out = fopen(pLog->zFilename, "a");
|
||
+ if( pLog->out==0 ){
|
||
+ sqlite3_mutex_leave(pMutex);
|
||
+ sqlite3_free(pLog);
|
||
+ return 0;
|
||
+ }
|
||
+ pLog->nFilename = nName;
|
||
+ pLog[1].out = pLog[0].out;
|
||
+ pLog->ppPrev = &allLogs;
|
||
+ if( allLogs ) allLogs->ppPrev = &pLog->pNext;
|
||
+ pLog->pNext = allLogs;
|
||
+ allLogs = pLog;
|
||
+ }
|
||
+ sqlite3_mutex_leave(pMutex);
|
||
+ if( pTemp ){
|
||
+ sqlite3_free(pTemp);
|
||
+ }else{
|
||
+#if SQLITE_OS_UNIX || defined(__CYGWIN__)
|
||
+ char zHost[200];
|
||
+ zHost[0] = 0;
|
||
+ gethostname(zHost, sizeof(zHost)-1);
|
||
+ zHost[sizeof(zHost)-1] = 0;
|
||
+ vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
|
||
+#endif
|
||
+ }
|
||
+ if( pLog && isJournal ) pLog++;
|
||
+ pLog->nRef++;
|
||
+ return pLog;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Close an vlog-file.
|
||
+*/
|
||
+static int vlogClose(sqlite3_file *pFile){
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ int rc = SQLITE_OK;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+
|
||
+ tStart = vlog_time();
|
||
+ if( p->pReal->pMethods ){
|
||
+ rc = p->pReal->pMethods->xClose(p->pReal);
|
||
+ }
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
|
||
+ vlogLogClose(p->pLog);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Compute signature for a block of content.
|
||
+**
|
||
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
|
||
+** the entire block.
|
||
+**
|
||
+** For blocks of more than 16 bytes, the signature is a hex dump of the
|
||
+** first 8 bytes followed by a 64-bit has of the entire block.
|
||
+*/
|
||
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
|
||
+ unsigned int s0 = 0, s1 = 0;
|
||
+ unsigned int *pI;
|
||
+ int i;
|
||
+ if( n<=16 ){
|
||
+ for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
||
+ }else{
|
||
+ pI = (unsigned int*)p;
|
||
+ for(i=0; i<n-7; i+=8){
|
||
+ s0 += pI[0] + s1;
|
||
+ s1 += pI[1] + s0;
|
||
+ pI += 2;
|
||
+ }
|
||
+ for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
|
||
+ sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Convert a big-endian 32-bit integer into a native integer
|
||
+*/
|
||
+static int bigToNative(const unsigned char *x){
|
||
+ return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
|
||
+}
|
||
+
|
||
+/*
|
||
+** Read data from an vlog-file.
|
||
+*/
|
||
+static int vlogRead(
|
||
+ sqlite3_file *pFile,
|
||
+ void *zBuf,
|
||
+ int iAmt,
|
||
+ sqlite_int64 iOfst
|
||
+){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ char zSig[40];
|
||
+
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ if( rc==SQLITE_OK ){
|
||
+ vlogSignature(zBuf, iAmt, zSig);
|
||
+ }else{
|
||
+ zSig[0] = 0;
|
||
+ }
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
|
||
+ if( rc==SQLITE_OK
|
||
+ && p->pLog
|
||
+ && p->pLog->zFilename
|
||
+ && iOfst<=24
|
||
+ && iOfst+iAmt>=28
|
||
+ ){
|
||
+ unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
|
||
+ unsigned iCtr, nFree = -1;
|
||
+ char *zFree = 0;
|
||
+ char zStr[12];
|
||
+ iCtr = bigToNative(x);
|
||
+ if( iOfst+iAmt>=40 ){
|
||
+ zFree = zStr;
|
||
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
||
+ nFree = bigToNative(x+12);
|
||
+ }
|
||
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Write data to an vlog-file.
|
||
+*/
|
||
+static int vlogWrite(
|
||
+ sqlite3_file *pFile,
|
||
+ const void *z,
|
||
+ int iAmt,
|
||
+ sqlite_int64 iOfst
|
||
+){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ char zSig[40];
|
||
+
|
||
+ tStart = vlog_time();
|
||
+ vlogSignature((unsigned char*)z, iAmt, zSig);
|
||
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
|
||
+ if( rc==SQLITE_OK
|
||
+ && p->pLog
|
||
+ && p->pLog->zFilename
|
||
+ && iOfst<=24
|
||
+ && iOfst+iAmt>=28
|
||
+ ){
|
||
+ unsigned char *x = ((unsigned char*)z)+(24-iOfst);
|
||
+ unsigned iCtr, nFree = -1;
|
||
+ char *zFree = 0;
|
||
+ char zStr[12];
|
||
+ iCtr = bigToNative(x);
|
||
+ if( iOfst+iAmt>=40 ){
|
||
+ zFree = zStr;
|
||
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
|
||
+ nFree = bigToNative(x+12);
|
||
+ }
|
||
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Truncate an vlog-file.
|
||
+*/
|
||
+static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Sync an vlog-file.
|
||
+*/
|
||
+static int vlogSync(sqlite3_file *pFile, int flags){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the current file-size of an vlog-file.
|
||
+*/
|
||
+static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Lock an vlog-file.
|
||
+*/
|
||
+static int vlogLock(sqlite3_file *pFile, int eLock){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Unlock an vlog-file.
|
||
+*/
|
||
+static int vlogUnlock(sqlite3_file *pFile, int eLock){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
|
||
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Check if another file-handle holds a RESERVED lock on an vlog-file.
|
||
+*/
|
||
+static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
|
||
+ *pResOut, -1, "", rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** File control method. For custom operations on an vlog-file.
|
||
+*/
|
||
+static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ int rc;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||
+ *(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
|
||
+ }
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ if( op==SQLITE_FCNTL_TRACE ){
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
|
||
+ }else if( op==SQLITE_FCNTL_PRAGMA ){
|
||
+ const char **azArg = (const char **)pArg;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
|
||
+ }else if( op==SQLITE_FCNTL_SIZE_HINT ){
|
||
+ sqlite3_int64 sz = *(sqlite3_int64*)pArg;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
|
||
+ }else{
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the sector-size in bytes for an vlog-file.
|
||
+*/
|
||
+static int vlogSectorSize(sqlite3_file *pFile){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the device characteristic flags supported by an vlog-file.
|
||
+*/
|
||
+static int vlogDeviceCharacteristics(sqlite3_file *pFile){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vlogShmMap(sqlite3_file*pFile,int a,int b,int c, void volatile **d){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xShmMap(p->pReal, a, b, c, d);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMMAP", a, b, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vlogShmLock(sqlite3_file*pFile, int a, int b, int c){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xShmLock(p->pReal, a, b, c);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, b, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static void vlogShmBarrier(sqlite3_file*pFile){
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ p->pReal->pMethods->xShmBarrier(p->pReal);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", -1, -1, 0, 0);
|
||
+}
|
||
+
|
||
+static int vlogShmUnmap(sqlite3_file*pFile, int a){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xShmUnmap(p->pReal, a);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vlogFetch(sqlite3_file*pFile, sqlite3_int64 a, int b, void** c){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xFetch(p->pReal, a, b, c);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, b, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vlogUnfetch(sqlite3_file*pFile, sqlite3_int64 a, void* b){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogFile *p = (VLogFile *)pFile;
|
||
+ tStart = vlog_time();
|
||
+ rc = p->pReal->pMethods->xUnfetch(p->pReal, a, b);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "SHMLOCK", a, -1, 0, rc);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open an vlog file handle.
|
||
+*/
|
||
+static int vlogOpen(
|
||
+ sqlite3_vfs *pVfs,
|
||
+ const char *zName,
|
||
+ sqlite3_file *pFile,
|
||
+ int flags,
|
||
+ int *pOutFlags
|
||
+){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ sqlite3_int64 iArg2;
|
||
+ VLogFile *p = (VLogFile*)pFile;
|
||
+
|
||
+ p->pReal = (sqlite3_file*)&p[1];
|
||
+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
|
||
+ p->pLog = vlogLogOpen(zName);
|
||
+ }else{
|
||
+ p->pLog = 0;
|
||
+ }
|
||
+ tStart = vlog_time();
|
||
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ iArg2 = pOutFlags ? *pOutFlags : -1;
|
||
+ vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
|
||
+ if( rc==SQLITE_OK ){
|
||
+ pFile->pMethods = &vlog_io_methods;
|
||
+ }else{
|
||
+ if( p->pLog ) vlogLogClose(p->pLog);
|
||
+ p->pLog = 0;
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Delete the file located at zPath. If the dirSync argument is true,
|
||
+** ensure the file-system modifications are synced to disk before
|
||
+** returning.
|
||
+*/
|
||
+static int vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogLog *pLog;
|
||
+ tStart = vlog_time();
|
||
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ pLog = vlogLogOpen(zPath);
|
||
+ vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
|
||
+ vlogLogClose(pLog);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Test for access permissions. Return true if the requested permission
|
||
+** is available, or false otherwise.
|
||
+*/
|
||
+static int vlogAccess(
|
||
+ sqlite3_vfs *pVfs,
|
||
+ const char *zPath,
|
||
+ int flags,
|
||
+ int *pResOut
|
||
+){
|
||
+ int rc;
|
||
+ sqlite3_uint64 tStart, tElapse;
|
||
+ VLogLog *pLog;
|
||
+ tStart = vlog_time();
|
||
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
|
||
+ tElapse = vlog_time() - tStart;
|
||
+ pLog = vlogLogOpen(zPath);
|
||
+ vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
|
||
+ vlogLogClose(pLog);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Populate buffer zOut with the full canonical pathname corresponding
|
||
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
||
+** of at least (INST_MAX_PATHNAME+1) bytes.
|
||
+*/
|
||
+static int vlogFullPathname(
|
||
+ sqlite3_vfs *pVfs,
|
||
+ const char *zPath,
|
||
+ int nOut,
|
||
+ char *zOut
|
||
+){
|
||
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Open the dynamic library located at zPath and return a handle.
|
||
+*/
|
||
+static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
||
+** utf-8 string describing the most recent error encountered associated
|
||
+** with dynamic libraries.
|
||
+*/
|
||
+static void vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
||
+*/
|
||
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Close the dynamic library handle pHandle.
|
||
+*/
|
||
+static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Populate the buffer pointed to by zBufOut with nByte bytes of
|
||
+** random data.
|
||
+*/
|
||
+static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Sleep for nMicro microseconds. Return the number of microseconds
|
||
+** actually slept.
|
||
+*/
|
||
+static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
|
||
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the current time as a Julian Day number in *pTimeOut.
|
||
+*/
|
||
+static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
|
||
+}
|
||
+
|
||
+static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
|
||
+}
|
||
+static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
|
||
+}
|
||
+static int vlogSetSystemCall(sqlite3_vfs*pVfs, const char *zName, sqlite3_syscall_ptr p){
|
||
+ return REALVFS(pVfs)->xSetSystemCall(REALVFS(pVfs), zName, p);
|
||
+}
|
||
+static sqlite3_syscall_ptr vlogGetSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
||
+ return REALVFS(pVfs)->xGetSystemCall(REALVFS(pVfs), zName);
|
||
+}
|
||
+static const char *vlogNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
||
+ return REALVFS(pVfs)->xNextSystemCall(REALVFS(pVfs), zName);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Register vfslog as the default VFS for this process.
|
||
+*/
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_register_vfslog(const char *zArg){
|
||
+ vlog_vfs.pVfs = sqlite3_vfs_find(0);
|
||
+ if( vlog_vfs.pVfs==&vlog_vfs.base ) return 0; /* Already registered. */
|
||
+ vlog_vfs.base.iVersion = vlog_vfs.pVfs->iVersion;
|
||
+ vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
|
||
+ vlog_vfs.base.mxPathname = vlog_vfs.pVfs->mxPathname;
|
||
+ return sqlite3_vfs_register(&vlog_vfs.base, 1);
|
||
+}
|
||
--- origsrc/sqlite-autoconf-3080802/vtshim.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/vtshim.c 2015-01-31 00:31:56.683159600 +0100
|
||
@@ -0,0 +1,561 @@
|
||
+/*
|
||
+** 2013-06-12
|
||
+**
|
||
+** 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.
|
||
+**
|
||
+*************************************************************************
|
||
+**
|
||
+** A shim that sits between the SQLite virtual table interface and
|
||
+** runtimes with garbage collector based memory management.
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+/* Forward references */
|
||
+typedef struct vtshim_aux vtshim_aux;
|
||
+typedef struct vtshim_vtab vtshim_vtab;
|
||
+typedef struct vtshim_cursor vtshim_cursor;
|
||
+
|
||
+
|
||
+/* The vtshim_aux argument is the auxiliary parameter that is passed
|
||
+** into sqlite3_create_module_v2().
|
||
+*/
|
||
+struct vtshim_aux {
|
||
+ void *pChildAux; /* pAux for child virtual tables */
|
||
+ void (*xChildDestroy)(void*); /* Destructor for pChildAux */
|
||
+ sqlite3_module *pMod; /* Methods for child virtual tables */
|
||
+ sqlite3 *db; /* The database to which we are attached */
|
||
+ char *zName; /* Name of the module */
|
||
+ int bDisposed; /* True if disposed */
|
||
+ vtshim_vtab *pAllVtab; /* List of all vtshim_vtab objects */
|
||
+ sqlite3_module sSelf; /* Methods used by this shim */
|
||
+};
|
||
+
|
||
+/* A vtshim virtual table object */
|
||
+struct vtshim_vtab {
|
||
+ sqlite3_vtab base; /* Base class - must be first */
|
||
+ sqlite3_vtab *pChild; /* Child virtual table */
|
||
+ vtshim_aux *pAux; /* Pointer to vtshim_aux object */
|
||
+ vtshim_cursor *pAllCur; /* List of all cursors */
|
||
+ vtshim_vtab **ppPrev; /* Previous on list */
|
||
+ vtshim_vtab *pNext; /* Next on list */
|
||
+};
|
||
+
|
||
+/* A vtshim cursor object */
|
||
+struct vtshim_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ sqlite3_vtab_cursor *pChild; /* Cursor generated by the managed subclass */
|
||
+ vtshim_cursor **ppPrev; /* Previous on list of all cursors */
|
||
+ vtshim_cursor *pNext; /* Next on list of all cursors */
|
||
+};
|
||
+
|
||
+/* Macro used to copy the child vtable error message to outer vtable */
|
||
+#define VTSHIM_COPY_ERRMSG() \
|
||
+ do { \
|
||
+ sqlite3_free(pVtab->base.zErrMsg); \
|
||
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", pVtab->pChild->zErrMsg); \
|
||
+ } while (0)
|
||
+
|
||
+/* Methods for the vtshim module */
|
||
+static int vtshimCreate(
|
||
+ sqlite3 *db,
|
||
+ void *ppAux,
|
||
+ int argc,
|
||
+ const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
||
+ vtshim_vtab *pNew;
|
||
+ int rc;
|
||
+
|
||
+ assert( db==pAux->db );
|
||
+ if( pAux->bDisposed ){
|
||
+ if( pzErr ){
|
||
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
||
+ pAux->zName);
|
||
+ }
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
||
+ *ppVtab = (sqlite3_vtab*)pNew;
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv,
|
||
+ &pNew->pChild, pzErr);
|
||
+ if( rc ){
|
||
+ sqlite3_free(pNew);
|
||
+ *ppVtab = 0;
|
||
+ }
|
||
+ pNew->pAux = pAux;
|
||
+ pNew->ppPrev = &pAux->pAllVtab;
|
||
+ pNew->pNext = pAux->pAllVtab;
|
||
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
||
+ pAux->pAllVtab = pNew;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimConnect(
|
||
+ sqlite3 *db,
|
||
+ void *ppAux,
|
||
+ int argc,
|
||
+ const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
||
+ vtshim_vtab *pNew;
|
||
+ int rc;
|
||
+
|
||
+ assert( db==pAux->db );
|
||
+ if( pAux->bDisposed ){
|
||
+ if( pzErr ){
|
||
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
||
+ pAux->zName);
|
||
+ }
|
||
+ return SQLITE_ERROR;
|
||
+ }
|
||
+ pNew = sqlite3_malloc( sizeof(*pNew) );
|
||
+ *ppVtab = (sqlite3_vtab*)pNew;
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv,
|
||
+ &pNew->pChild, pzErr);
|
||
+ if( rc ){
|
||
+ sqlite3_free(pNew);
|
||
+ *ppVtab = 0;
|
||
+ }
|
||
+ pNew->pAux = pAux;
|
||
+ pNew->ppPrev = &pAux->pAllVtab;
|
||
+ pNew->pNext = pAux->pAllVtab;
|
||
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
||
+ pAux->pAllVtab = pNew;
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimBestIndex(
|
||
+ sqlite3_vtab *pBase,
|
||
+ sqlite3_index_info *pIdxInfo
|
||
+){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xBestIndex(pVtab->pChild, pIdxInfo);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimDisconnect(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc = SQLITE_OK;
|
||
+ if( !pAux->bDisposed ){
|
||
+ rc = pAux->pMod->xDisconnect(pVtab->pChild);
|
||
+ }
|
||
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
||
+ *pVtab->ppPrev = pVtab->pNext;
|
||
+ sqlite3_free(pVtab);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimDestroy(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc = SQLITE_OK;
|
||
+ if( !pAux->bDisposed ){
|
||
+ rc = pAux->pMod->xDestroy(pVtab->pChild);
|
||
+ }
|
||
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
||
+ *pVtab->ppPrev = pVtab->pNext;
|
||
+ sqlite3_free(pVtab);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ vtshim_cursor *pCur;
|
||
+ int rc;
|
||
+ *ppCursor = 0;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
|
||
+ if( rc ){
|
||
+ sqlite3_free(pCur);
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ return rc;
|
||
+ }
|
||
+ pCur->pChild->pVtab = pVtab->pChild;
|
||
+ *ppCursor = &pCur->base;
|
||
+ pCur->ppPrev = &pVtab->pAllCur;
|
||
+ if( pVtab->pAllCur ) pVtab->pAllCur->ppPrev = &pCur->pNext;
|
||
+ pCur->pNext = pVtab->pAllCur;
|
||
+ pVtab->pAllCur = pCur;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+static int vtshimClose(sqlite3_vtab_cursor *pX){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc = SQLITE_OK;
|
||
+ if( !pAux->bDisposed ){
|
||
+ rc = pAux->pMod->xClose(pCur->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ }
|
||
+ if( pCur->pNext ) pCur->pNext->ppPrev = pCur->ppPrev;
|
||
+ *pCur->ppPrev = pCur->pNext;
|
||
+ sqlite3_free(pCur);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimFilter(
|
||
+ sqlite3_vtab_cursor *pX,
|
||
+ int idxNum,
|
||
+ const char *idxStr,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xFilter(pCur->pChild, idxNum, idxStr, argc, argv);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimNext(sqlite3_vtab_cursor *pX){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xNext(pCur->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimEof(sqlite3_vtab_cursor *pX){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return 1;
|
||
+ rc = pAux->pMod->xEof(pCur->pChild);
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimColumn(sqlite3_vtab_cursor *pX, sqlite3_context *ctx, int i){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xColumn(pCur->pChild, ctx, i);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimRowid(sqlite3_vtab_cursor *pX, sqlite3_int64 *pRowid){
|
||
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xRowid(pCur->pChild, pRowid);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimUpdate(
|
||
+ sqlite3_vtab *pBase,
|
||
+ int argc,
|
||
+ sqlite3_value **argv,
|
||
+ sqlite3_int64 *pRowid
|
||
+){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xUpdate(pVtab->pChild, argc, argv, pRowid);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimBegin(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xBegin(pVtab->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimSync(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xSync(pVtab->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimCommit(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xCommit(pVtab->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimRollback(sqlite3_vtab *pBase){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xRollback(pVtab->pChild);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimFindFunction(
|
||
+ sqlite3_vtab *pBase,
|
||
+ int nArg,
|
||
+ const char *zName,
|
||
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||
+ void **ppArg
|
||
+){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return 0;
|
||
+ rc = pAux->pMod->xFindFunction(pVtab->pChild, nArg, zName, pxFunc, ppArg);
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimRename(sqlite3_vtab *pBase, const char *zNewName){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xRename(pVtab->pChild, zNewName);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimSavepoint(sqlite3_vtab *pBase, int n){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xSavepoint(pVtab->pChild, n);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimRelease(sqlite3_vtab *pBase, int n){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xRelease(pVtab->pChild, n);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int vtshimRollbackTo(sqlite3_vtab *pBase, int n){
|
||
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||
+ vtshim_aux *pAux = pVtab->pAux;
|
||
+ int rc;
|
||
+ if( pAux->bDisposed ) return SQLITE_ERROR;
|
||
+ rc = pAux->pMod->xRollbackTo(pVtab->pChild, n);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ VTSHIM_COPY_ERRMSG();
|
||
+ }
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+/* The destructor function for a disposible module */
|
||
+static void vtshimAuxDestructor(void *pXAux){
|
||
+ vtshim_aux *pAux = (vtshim_aux*)pXAux;
|
||
+ assert( pAux->pAllVtab==0 );
|
||
+ if( !pAux->bDisposed && pAux->xChildDestroy ){
|
||
+ pAux->xChildDestroy(pAux->pChildAux);
|
||
+ pAux->xChildDestroy = 0;
|
||
+ }
|
||
+ sqlite3_free(pAux->zName);
|
||
+ sqlite3_free(pAux->pMod);
|
||
+ sqlite3_free(pAux);
|
||
+}
|
||
+
|
||
+static int vtshimCopyModule(
|
||
+ const sqlite3_module *pMod, /* Source module to be copied */
|
||
+ sqlite3_module **ppMod /* Destination for copied module */
|
||
+){
|
||
+ sqlite3_module *p;
|
||
+ if( !pMod || !ppMod ) return SQLITE_ERROR;
|
||
+ p = sqlite3_malloc( sizeof(*p) );
|
||
+ if( p==0 ) return SQLITE_NOMEM;
|
||
+ memcpy(p, pMod, sizeof(*p));
|
||
+ *ppMod = p;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+void *sqlite3_create_disposable_module(
|
||
+ sqlite3 *db, /* SQLite connection to register module with */
|
||
+ const char *zName, /* Name of the module */
|
||
+ const sqlite3_module *p, /* Methods for the module */
|
||
+ void *pClientData, /* Client data for xCreate/xConnect */
|
||
+ void(*xDestroy)(void*) /* Module destructor function */
|
||
+){
|
||
+ vtshim_aux *pAux;
|
||
+ sqlite3_module *pMod;
|
||
+ int rc;
|
||
+ pAux = sqlite3_malloc( sizeof(*pAux) );
|
||
+ if( pAux==0 ){
|
||
+ if( xDestroy ) xDestroy(pClientData);
|
||
+ return 0;
|
||
+ }
|
||
+ rc = vtshimCopyModule(p, &pMod);
|
||
+ if( rc!=SQLITE_OK ){
|
||
+ sqlite3_free(pAux);
|
||
+ return 0;
|
||
+ }
|
||
+ pAux->pChildAux = pClientData;
|
||
+ pAux->xChildDestroy = xDestroy;
|
||
+ pAux->pMod = pMod;
|
||
+ pAux->db = db;
|
||
+ pAux->zName = sqlite3_mprintf("%s", zName);
|
||
+ pAux->bDisposed = 0;
|
||
+ pAux->pAllVtab = 0;
|
||
+ pAux->sSelf.iVersion = p->iVersion<=2 ? p->iVersion : 2;
|
||
+ pAux->sSelf.xCreate = p->xCreate ? vtshimCreate : 0;
|
||
+ pAux->sSelf.xConnect = p->xConnect ? vtshimConnect : 0;
|
||
+ pAux->sSelf.xBestIndex = p->xBestIndex ? vtshimBestIndex : 0;
|
||
+ pAux->sSelf.xDisconnect = p->xDisconnect ? vtshimDisconnect : 0;
|
||
+ pAux->sSelf.xDestroy = p->xDestroy ? vtshimDestroy : 0;
|
||
+ pAux->sSelf.xOpen = p->xOpen ? vtshimOpen : 0;
|
||
+ pAux->sSelf.xClose = p->xClose ? vtshimClose : 0;
|
||
+ pAux->sSelf.xFilter = p->xFilter ? vtshimFilter : 0;
|
||
+ pAux->sSelf.xNext = p->xNext ? vtshimNext : 0;
|
||
+ pAux->sSelf.xEof = p->xEof ? vtshimEof : 0;
|
||
+ pAux->sSelf.xColumn = p->xColumn ? vtshimColumn : 0;
|
||
+ pAux->sSelf.xRowid = p->xRowid ? vtshimRowid : 0;
|
||
+ pAux->sSelf.xUpdate = p->xUpdate ? vtshimUpdate : 0;
|
||
+ pAux->sSelf.xBegin = p->xBegin ? vtshimBegin : 0;
|
||
+ pAux->sSelf.xSync = p->xSync ? vtshimSync : 0;
|
||
+ pAux->sSelf.xCommit = p->xCommit ? vtshimCommit : 0;
|
||
+ pAux->sSelf.xRollback = p->xRollback ? vtshimRollback : 0;
|
||
+ pAux->sSelf.xFindFunction = p->xFindFunction ? vtshimFindFunction : 0;
|
||
+ pAux->sSelf.xRename = p->xRename ? vtshimRename : 0;
|
||
+ if( p->iVersion>=2 ){
|
||
+ pAux->sSelf.xSavepoint = p->xSavepoint ? vtshimSavepoint : 0;
|
||
+ pAux->sSelf.xRelease = p->xRelease ? vtshimRelease : 0;
|
||
+ pAux->sSelf.xRollbackTo = p->xRollbackTo ? vtshimRollbackTo : 0;
|
||
+ }else{
|
||
+ pAux->sSelf.xSavepoint = 0;
|
||
+ pAux->sSelf.xRelease = 0;
|
||
+ pAux->sSelf.xRollbackTo = 0;
|
||
+ }
|
||
+ rc = sqlite3_create_module_v2(db, zName, &pAux->sSelf,
|
||
+ pAux, vtshimAuxDestructor);
|
||
+ return rc==SQLITE_OK ? (void*)pAux : 0;
|
||
+}
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+void sqlite3_dispose_module(void *pX){
|
||
+ vtshim_aux *pAux = (vtshim_aux*)pX;
|
||
+ if( !pAux->bDisposed ){
|
||
+ vtshim_vtab *pVtab;
|
||
+ vtshim_cursor *pCur;
|
||
+ for(pVtab=pAux->pAllVtab; pVtab; pVtab=pVtab->pNext){
|
||
+ for(pCur=pVtab->pAllCur; pCur; pCur=pCur->pNext){
|
||
+ pAux->pMod->xClose(pCur->pChild);
|
||
+ }
|
||
+ pAux->pMod->xDisconnect(pVtab->pChild);
|
||
+ }
|
||
+ pAux->bDisposed = 1;
|
||
+ if( pAux->xChildDestroy ){
|
||
+ pAux->xChildDestroy(pAux->pChildAux);
|
||
+ pAux->xChildDestroy = 0;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_vtshim_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/wholenumber.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/wholenumber.c 2015-01-31 00:31:56.693160200 +0100
|
||
@@ -0,0 +1,288 @@
|
||
+/*
|
||
+** 2011 April 02
|
||
+**
|
||
+** 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 a virtual table that returns the whole numbers
|
||
+** between 1 and 4294967295, inclusive.
|
||
+**
|
||
+** Example:
|
||
+**
|
||
+** CREATE VIRTUAL TABLE nums USING wholenumber;
|
||
+** SELECT value FROM nums WHERE value<10;
|
||
+**
|
||
+** Results in:
|
||
+**
|
||
+** 1 2 3 4 5 6 7 8 9
|
||
+*/
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+#include <assert.h>
|
||
+#include <string.h>
|
||
+
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+
|
||
+
|
||
+/* A wholenumber cursor object */
|
||
+typedef struct wholenumber_cursor wholenumber_cursor;
|
||
+struct wholenumber_cursor {
|
||
+ sqlite3_vtab_cursor base; /* Base class - must be first */
|
||
+ sqlite3_int64 iValue; /* Current value */
|
||
+ sqlite3_int64 mxValue; /* Maximum value */
|
||
+};
|
||
+
|
||
+/* Methods for the wholenumber module */
|
||
+static int wholenumberConnect(
|
||
+ sqlite3 *db,
|
||
+ void *pAux,
|
||
+ int argc, const char *const*argv,
|
||
+ sqlite3_vtab **ppVtab,
|
||
+ char **pzErr
|
||
+){
|
||
+ sqlite3_vtab *pNew;
|
||
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||
+ if( pNew==0 ) return SQLITE_NOMEM;
|
||
+ sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
||
+ memset(pNew, 0, sizeof(*pNew));
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+/* Note that for this virtual table, the xCreate and xConnect
|
||
+** methods are identical. */
|
||
+
|
||
+static int wholenumberDisconnect(sqlite3_vtab *pVtab){
|
||
+ sqlite3_free(pVtab);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+/* The xDisconnect and xDestroy methods are also the same */
|
||
+
|
||
+
|
||
+/*
|
||
+** Open a new wholenumber cursor.
|
||
+*/
|
||
+static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||
+ wholenumber_cursor *pCur;
|
||
+ pCur = sqlite3_malloc( sizeof(*pCur) );
|
||
+ if( pCur==0 ) return SQLITE_NOMEM;
|
||
+ memset(pCur, 0, sizeof(*pCur));
|
||
+ *ppCursor = &pCur->base;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Close a wholenumber cursor.
|
||
+*/
|
||
+static int wholenumberClose(sqlite3_vtab_cursor *cur){
|
||
+ sqlite3_free(cur);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+
|
||
+/*
|
||
+** Advance a cursor to its next row of output
|
||
+*/
|
||
+static int wholenumberNext(sqlite3_vtab_cursor *cur){
|
||
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||
+ pCur->iValue++;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Return the value associated with a wholenumber.
|
||
+*/
|
||
+static int wholenumberColumn(
|
||
+ sqlite3_vtab_cursor *cur,
|
||
+ sqlite3_context *ctx,
|
||
+ int i
|
||
+){
|
||
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||
+ sqlite3_result_int64(ctx, pCur->iValue);
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** The rowid.
|
||
+*/
|
||
+static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||
+ *pRowid = pCur->iValue;
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
|
||
+** that the cursor has nothing more to output.
|
||
+*/
|
||
+static int wholenumberEof(sqlite3_vtab_cursor *cur){
|
||
+ wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||
+ return pCur->iValue>pCur->mxValue || pCur->iValue==0;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Called to "rewind" a cursor back to the beginning so that
|
||
+** it starts its output over again. Always called at least once
|
||
+** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call.
|
||
+**
|
||
+** idxNum Constraints
|
||
+** ------ ---------------------
|
||
+** 0 (none)
|
||
+** 1 value > $argv0
|
||
+** 2 value >= $argv0
|
||
+** 4 value < $argv0
|
||
+** 8 value <= $argv0
|
||
+**
|
||
+** 5 value > $argv0 AND value < $argv1
|
||
+** 6 value >= $argv0 AND value < $argv1
|
||
+** 9 value > $argv0 AND value <= $argv1
|
||
+** 10 value >= $argv0 AND value <= $argv1
|
||
+*/
|
||
+static int wholenumberFilter(
|
||
+ sqlite3_vtab_cursor *pVtabCursor,
|
||
+ int idxNum, const char *idxStr,
|
||
+ int argc, sqlite3_value **argv
|
||
+){
|
||
+ wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
|
||
+ sqlite3_int64 v;
|
||
+ int i = 0;
|
||
+ pCur->iValue = 1;
|
||
+ pCur->mxValue = 0xffffffff; /* 4294967295 */
|
||
+ if( idxNum & 3 ){
|
||
+ v = sqlite3_value_int64(argv[0]) + (idxNum&1);
|
||
+ if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
|
||
+ i++;
|
||
+ }
|
||
+ if( idxNum & 12 ){
|
||
+ v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
|
||
+ if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** Search for terms of these forms:
|
||
+**
|
||
+** (1) value > $value
|
||
+** (2) value >= $value
|
||
+** (4) value < $value
|
||
+** (8) value <= $value
|
||
+**
|
||
+** idxNum is an ORed combination of 1 or 2 with 4 or 8.
|
||
+*/
|
||
+static int wholenumberBestIndex(
|
||
+ sqlite3_vtab *tab,
|
||
+ sqlite3_index_info *pIdxInfo
|
||
+){
|
||
+ int i;
|
||
+ int idxNum = 0;
|
||
+ int argvIdx = 1;
|
||
+ int ltIdx = -1;
|
||
+ int gtIdx = -1;
|
||
+ const struct sqlite3_index_constraint *pConstraint;
|
||
+ pConstraint = pIdxInfo->aConstraint;
|
||
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||
+ if( pConstraint->usable==0 ) continue;
|
||
+ if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
|
||
+ idxNum |= 1;
|
||
+ ltIdx = i;
|
||
+ }
|
||
+ if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
|
||
+ idxNum |= 2;
|
||
+ ltIdx = i;
|
||
+ }
|
||
+ if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
||
+ idxNum |= 4;
|
||
+ gtIdx = i;
|
||
+ }
|
||
+ if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||
+ idxNum |= 8;
|
||
+ gtIdx = i;
|
||
+ }
|
||
+ }
|
||
+ pIdxInfo->idxNum = idxNum;
|
||
+ if( ltIdx>=0 ){
|
||
+ pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
|
||
+ pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
|
||
+ }
|
||
+ if( gtIdx>=0 ){
|
||
+ pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
|
||
+ pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
|
||
+ }
|
||
+ if( pIdxInfo->nOrderBy==1
|
||
+ && pIdxInfo->aOrderBy[0].desc==0
|
||
+ ){
|
||
+ pIdxInfo->orderByConsumed = 1;
|
||
+ }
|
||
+ if( (idxNum & 12)==0 ){
|
||
+ pIdxInfo->estimatedCost = (double)100000000;
|
||
+ }else if( (idxNum & 3)==0 ){
|
||
+ pIdxInfo->estimatedCost = (double)5;
|
||
+ }else{
|
||
+ pIdxInfo->estimatedCost = (double)1;
|
||
+ }
|
||
+ return SQLITE_OK;
|
||
+}
|
||
+
|
||
+/*
|
||
+** A virtual table module that provides read-only access to a
|
||
+** Tcl global variable namespace.
|
||
+*/
|
||
+static sqlite3_module wholenumberModule = {
|
||
+ 0, /* iVersion */
|
||
+ wholenumberConnect,
|
||
+ wholenumberConnect,
|
||
+ wholenumberBestIndex,
|
||
+ wholenumberDisconnect,
|
||
+ wholenumberDisconnect,
|
||
+ wholenumberOpen, /* xOpen - open a cursor */
|
||
+ wholenumberClose, /* xClose - close a cursor */
|
||
+ wholenumberFilter, /* xFilter - configure scan constraints */
|
||
+ wholenumberNext, /* xNext - advance a cursor */
|
||
+ wholenumberEof, /* xEof - check for end of scan */
|
||
+ wholenumberColumn, /* xColumn - read data */
|
||
+ wholenumberRowid, /* xRowid - read data */
|
||
+ 0, /* xUpdate */
|
||
+ 0, /* xBegin */
|
||
+ 0, /* xSync */
|
||
+ 0, /* xCommit */
|
||
+ 0, /* xRollback */
|
||
+ 0, /* xFindMethod */
|
||
+ 0, /* xRename */
|
||
+};
|
||
+
|
||
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_wholenumber_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
||
+#endif
|
||
+ return rc;
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ int rc = SQLITE_OK;
|
||
+ SQLITE_EXTENSION_INIT2(pApi);
|
||
+#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||
+ rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
||
+#endif
|
||
+ return rc;
|
||
+}
|
||
+#endif
|
||
--- origsrc/sqlite-autoconf-3080802/zlib.c 1970-01-01 01:00:00.000000000 +0100
|
||
+++ src/sqlite-autoconf-3080802/zlib.c 2015-01-31 00:31:56.702160700 +0100
|
||
@@ -0,0 +1,129 @@
|
||
+/*
|
||
+** 2014 January 6
|
||
+**
|
||
+** 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.
|
||
+**
|
||
+*************************************************************************
|
||
+** $Id: zlib.c,v 1.0 2013/01/06 11:13:11 jan.nijtmans Exp $
|
||
+**
|
||
+** This file implements an integration between the zlib library ,
|
||
+** an open-source compression library and SQLite. The integration uses
|
||
+** zlib to provide the following to SQLite:
|
||
+**
|
||
+** * An implementation of the SQL compress() and decompress()
|
||
+** functions.
|
||
+*/
|
||
+
|
||
+#include "sqlite3ext.h"
|
||
+SQLITE_EXTENSION_INIT1
|
||
+/* Include zlib headers */
|
||
+#include <zlib.h>
|
||
+
|
||
+/*
|
||
+** Implementation of the "compress(X)" SQL function. The input X is
|
||
+** compressed using zLib and the output is returned.
|
||
+*/
|
||
+static void zlibCompressFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *pIn;
|
||
+ unsigned char *pOut;
|
||
+ unsigned int nIn;
|
||
+ unsigned long int nOut;
|
||
+
|
||
+ pIn = sqlite3_value_blob(argv[0]);
|
||
+ nIn = sqlite3_value_bytes(argv[0]);
|
||
+ nOut = 13 + nIn + (nIn+999)/1000;
|
||
+ pOut = sqlite3_malloc( nOut+4 );
|
||
+ pOut[0] = nIn>>24 & 0xff;
|
||
+ pOut[1] = nIn>>16 & 0xff;
|
||
+ pOut[2] = nIn>>8 & 0xff;
|
||
+ pOut[3] = nIn & 0xff;
|
||
+ compress(&pOut[4], &nOut, pIn, nIn);
|
||
+ sqlite3_result_blob(context, pOut, nOut+4, sqlite3_free);
|
||
+}
|
||
+
|
||
+/*
|
||
+** Implementation of the "decompress(X)" SQL function. The argument X
|
||
+** is a blob which was obtained from compress(Y). The output will be
|
||
+** the value Y.
|
||
+*/
|
||
+static void zlibDecompressFunc(
|
||
+ sqlite3_context *context,
|
||
+ int argc,
|
||
+ sqlite3_value **argv
|
||
+){
|
||
+ const unsigned char *pIn;
|
||
+ unsigned char *pOut;
|
||
+ unsigned int nIn;
|
||
+ unsigned long int nOut;
|
||
+ int rc;
|
||
+
|
||
+ pIn = sqlite3_value_blob(argv[0]);
|
||
+ nIn = sqlite3_value_bytes(argv[0]);
|
||
+ nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3];
|
||
+ pOut = sqlite3_malloc( nOut+1 );
|
||
+ rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
|
||
+ if( rc==Z_OK ){
|
||
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||
+ }else{
|
||
+ sqlite3_result_error(context, "input is not zlib compressed", -1);
|
||
+ }
|
||
+}
|
||
+
|
||
+/*
|
||
+** Register the zlib extension functions with database db.
|
||
+*/
|
||
+static int sqlite3ZlibInit(sqlite3 *db){
|
||
+ const struct ZlibScalar {
|
||
+ const char *zName; /* Function name */
|
||
+ int nArg; /* Number of arguments */
|
||
+ int enc; /* Optimal text encoding */
|
||
+ void *pContext; /* sqlite3_user_data() context */
|
||
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||
+ } scalars[] = {
|
||
+ {"compress", 1, SQLITE_UTF8, 0, zlibCompressFunc},
|
||
+ {"decompress", 1, SQLITE_UTF8, 0, zlibDecompressFunc},
|
||
+ };
|
||
+
|
||
+ int rc = SQLITE_OK;
|
||
+ int i;
|
||
+
|
||
+ for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
||
+ const struct ZlibScalar *p = &scalars[i];
|
||
+ rc = sqlite3_create_function(
|
||
+ db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0
|
||
+ );
|
||
+ }
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+#ifdef _WIN32
|
||
+__declspec(dllexport)
|
||
+#endif
|
||
+int sqlite3_zlib_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi)
|
||
+ return sqlite3ZlibInit(db);
|
||
+}
|
||
+#if !defined(_WIN32) && !defined(SQLITE_TEST)
|
||
+int sqlite3_extension_init(
|
||
+ sqlite3 *db,
|
||
+ char **pzErrMsg,
|
||
+ const sqlite3_api_routines *pApi
|
||
+){
|
||
+ SQLITE_EXTENSION_INIT2(pApi)
|
||
+ return sqlite3ZlibInit(db);
|
||
+}
|
||
+#endif
|