-This repository contains the complete source code for the SQLite database
-engine. Some test scripts are also included. However, many other test scripts
+This repository contains the complete source code for the
+[SQLite database engine](https://sqlite.org/). Some test scripts
+are also included. However, many other test scripts
and most of the documentation are managed separately.
SQLite [does not use Git](https://sqlite.org/whynotgit.html).
If you are reading this on GitHub, then you are looking at an
unofficial mirror. See for the official
Index: configure
==================================================================
--- configure
+++ configure
@@ -909,10 +909,11 @@
enable_fts3
enable_fts4
enable_fts5
enable_json1
enable_update_limit
+enable_geopoly
enable_rtree
enable_session
enable_gcov
'
ac_precious_vars='build_alias
@@ -1561,10 +1562,11 @@
--enable-fts3 Enable the FTS3 extension
--enable-fts4 Enable the FTS4 extension
--enable-fts5 Enable the FTS5 extension
--enable-json1 Enable the JSON1 extension
--enable-update-limit Enable the UPDATE/DELETE LIMIT clause
+ --enable-geopoly Enable the GEOPOLY extension
--enable-rtree Enable the RTREE extension
--enable-session Enable the SESSION extension
--enable-gcov Enable coverage testing using gcov
Optional Packages:
@@ -3930,17 +3932,17 @@
if ${lt_cv_nm_interface+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
- (eval echo "\"\$as_me:3935: $ac_compile\"" >&5)
+ (eval echo "\"\$as_me:3937: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
- (eval echo "\"\$as_me:3938: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval echo "\"\$as_me:3940: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
- (eval echo "\"\$as_me:3941: output\"" >&5)
+ (eval echo "\"\$as_me:3943: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
fi
rm -f conftest*
@@ -5142,11 +5144,11 @@
fi
rm -rf conftest*
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5147 "configure"' > conftest.$ac_ext
+ echo '#line 5149 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
@@ -6667,15 +6669,15 @@
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:6672: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6674: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6676: \$? = $ac_status" >&5
+ echo "$as_me:6678: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
@@ -7006,15 +7008,15 @@
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7011: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7013: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:7015: \$? = $ac_status" >&5
+ echo "$as_me:7017: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
@@ -7111,15 +7113,15 @@
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7116: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7118: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7120: \$? = $ac_status" >&5
+ echo "$as_me:7122: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
@@ -7166,15 +7168,15 @@
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7171: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7173: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7175: \$? = $ac_status" >&5
+ echo "$as_me:7177: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
@@ -9546,11 +9548,11 @@
lt_cv_dlopen_self=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9551 "configure"
+#line 9553 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include
#endif
@@ -9642,11 +9644,11 @@
lt_cv_dlopen_self_static=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9647 "configure"
+#line 9649 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include
#endif
@@ -11607,10 +11609,24 @@
fi
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
fi
+
+#########
+# See whether we should enable GEOPOLY
+# Check whether --enable-geopoly was given.
+if test "${enable_geopoly+set}" = set; then :
+ enableval=$enable_geopoly; enable_geopoly=yes
+else
+ enable_geopoly=no
+fi
+
+if test "${enable_geopoly}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+ enable_rtree=yes
+fi
#########
# See whether we should enable RTREE
# Check whether --enable-rtree was given.
if test "${enable_rtree+set}" = set; then :
Index: configure.ac
==================================================================
--- configure.ac
+++ configure.ac
@@ -646,10 +646,20 @@
AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit],
[Enable the UPDATE/DELETE LIMIT clause]))
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
fi
+
+#########
+# See whether we should enable GEOPOLY
+AC_ARG_ENABLE(geopoly, AC_HELP_STRING([--enable-geopoly],
+ [Enable the GEOPOLY extension]),
+ [enable_geopoly=yes],[enable_geopoly=no])
+if test "${enable_geopoly}" = "yes" ; then
+ OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_GEOPOLY"
+ enable_rtree=yes
+fi
#########
# See whether we should enable RTREE
AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
[Enable the RTREE extension]))
Index: ext/misc/completion.c
==================================================================
--- ext/misc/completion.c
+++ ext/misc/completion.c
@@ -363,19 +363,18 @@
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
- iArg++;
+ iArg = 1;
}
if( idxNum & 2 ){
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
}
- iArg++;
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
i--;
Index: ext/misc/json1.c
==================================================================
--- ext/misc/json1.c
+++ ext/misc/json1.c
@@ -170,10 +170,11 @@
u32 *aUp; /* Index of parent of each node */
u8 oom; /* Set to true if out of memory */
u8 nErr; /* Number of errors seen */
u16 iDepth; /* Nesting depth */
int nJson; /* Length of the zJson string in bytes */
+ u32 iHold; /* Replace cache line with the lowest iHold value */
};
/*
** Maximum nesting depth of JSON for this implementation.
**
@@ -974,11 +975,12 @@
}
/*
** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
*/
-#define JSON_CACHE_ID (-429938)
+#define JSON_CACHE_ID (-429938) /* First cache entry */
+#define JSON_CACHE_SZ 4 /* Max number of cache entries */
/*
** Obtain a complete parse of the JSON found in the first argument
** of the argv array. Use the sqlite3_get_auxdata() cache for this
** parse if it is available. If the cache is not available or if it
@@ -986,36 +988,64 @@
** and also register the new parse so that it will be available for
** future sqlite3_get_auxdata() calls.
*/
static JsonParse *jsonParseCached(
sqlite3_context *pCtx,
- sqlite3_value **argv
+ sqlite3_value **argv,
+ sqlite3_context *pErrCtx
){
const char *zJson = (const char*)sqlite3_value_text(argv[0]);
int nJson = sqlite3_value_bytes(argv[0]);
JsonParse *p;
+ JsonParse *pMatch = 0;
+ int iKey;
+ int iMinKey = 0;
+ u32 iMinHold = 0xffffffff;
+ u32 iMaxHold = 0;
if( zJson==0 ) return 0;
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
- if( p && p->nJson==nJson && memcmp(p->zJson,zJson,nJson)==0 ){
- p->nErr = 0;
- return p; /* The cached entry matches, so return it */
+ for(iKey=0; iKeynJson==nJson
+ && memcmp(p->zJson,zJson,nJson)==0
+ ){
+ p->nErr = 0;
+ pMatch = p;
+ }else if( p->iHoldiHold;
+ iMinKey = iKey;
+ }
+ if( p->iHold>iMaxHold ){
+ iMaxHold = p->iHold;
+ }
+ }
+ if( pMatch ){
+ pMatch->nErr = 0;
+ pMatch->iHold = iMaxHold+1;
+ return pMatch;
}
p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
if( p==0 ){
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
p->zJson = (char*)&p[1];
memcpy((char*)p->zJson, zJson, nJson+1);
- if( jsonParse(p, pCtx, p->zJson) ){
+ if( jsonParse(p, pErrCtx, p->zJson) ){
sqlite3_free(p);
return 0;
}
p->nJson = nJson;
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID, p, (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID);
+ p->iHold = iMaxHold+1;
+ sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
+ (void(*)(void*))jsonParseFree);
+ return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
** a match.
@@ -1384,11 +1414,11 @@
JsonParse *p; /* The parse */
sqlite3_int64 n = 0;
u32 i;
JsonNode *pNode;
- p = jsonParseCached(ctx, argv);
+ p = jsonParseCached(ctx, argv, ctx);
if( p==0 ) return;
assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
pNode = jsonLookup(p, zPath, 0, ctx);
@@ -1425,11 +1455,11 @@
const char *zPath;
JsonString jx;
int i;
if( argc<2 ) return;
- p = jsonParseCached(ctx, argv);
+ p = jsonParseCached(ctx, argv, ctx);
if( p==0 ) return;
jsonInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=1; iaNode;
}
if( pNode ){
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
- jsonParseReset(&x);
}
/*
** json_valid(JSON)
**
@@ -1761,19 +1790,14 @@
static void jsonValidFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The parse */
- int rc = 0;
-
+ JsonParse *p; /* The parse */
UNUSED_PARAM(argc);
- if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
- rc = 1;
- }
- jsonParseReset(&x);
- sqlite3_result_int(ctx, rc);
+ p = jsonParseCached(ctx, argv, 0);
+ sqlite3_result_int(ctx, p!=0);
}
/****************************************************************************
** Aggregate SQL function implementations
ADDED ext/rtree/geopoly.c
Index: ext/rtree/geopoly.c
==================================================================
--- /dev/null
+++ ext/rtree/geopoly.c
@@ -0,0 +1,1659 @@
+/*
+** 2018-05-25
+**
+** 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 an alternative R-Tree virtual table that
+** uses polygons to express the boundaries of 2-dimensional objects.
+**
+** This file is #include-ed onto the end of "rtree.c" so that it has
+** access to all of the R-Tree internals.
+*/
+#include
+
+/* Enable -DGEOPOLY_ENABLE_DEBUG for debugging facilities */
+#ifdef GEOPOLY_ENABLE_DEBUG
+ static int geo_debug = 0;
+# define GEODEBUG(X) if(geo_debug)printf X
+#else
+# define GEODEBUG(X)
+#endif
+
+#ifndef JSON_NULL /* The following stuff repeats things found in json1 */
+/*
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
+** to pass signed char values.
+*/
+#ifdef sqlite3Isdigit
+ /* Use the SQLite core versions if this routine is part of the
+ ** SQLite amalgamation */
+# define safe_isdigit(x) sqlite3Isdigit(x)
+# define safe_isalnum(x) sqlite3Isalnum(x)
+# define safe_isxdigit(x) sqlite3Isxdigit(x)
+#else
+ /* Use the standard library for separate compilation */
+#include /* amalgamator: keep */
+# define safe_isdigit(x) isdigit((unsigned char)(x))
+# define safe_isalnum(x) isalnum((unsigned char)(x))
+# define safe_isxdigit(x) isxdigit((unsigned char)(x))
+#endif
+
+/*
+** Growing our own isspace() routine this way is twice as fast as
+** the library isspace() function.
+*/
+static const char geopolyIsSpace[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x])
+#endif /* JSON NULL - back to original code */
+
+/* Compiler and version */
+#ifndef GCC_VERSION
+#if defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
+# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
+#else
+# define GCC_VERSION 0
+#endif
+#endif
+#ifndef MSVC_VERSION
+#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
+# define MSVC_VERSION _MSC_VER
+#else
+# define MSVC_VERSION 0
+#endif
+#endif
+
+/* Datatype for coordinates
+*/
+typedef float GeoCoord;
+
+/*
+** Internal representation of a polygon.
+**
+** The polygon consists of a sequence of vertexes. There is a line
+** segment between each pair of vertexes, and one final segment from
+** the last vertex back to the first. (This differs from the GeoJSON
+** standard in which the final vertex is a repeat of the first.)
+**
+** The polygon follows the right-hand rule. The area to the right of
+** each segment is "outside" and the area to the left is "inside".
+**
+** The on-disk representation consists of a 4-byte header followed by
+** the values. The 4-byte header is:
+**
+** encoding (1 byte) 0=big-endian, 1=little-endian
+** nvertex (3 bytes) Number of vertexes as a big-endian integer
+*/
+typedef struct GeoPoly GeoPoly;
+struct GeoPoly {
+ int nVertex; /* Number of vertexes */
+ unsigned char hdr[4]; /* Header for on-disk representation */
+ GeoCoord a[2]; /* 2*nVertex values. X (longitude) first, then Y */
+};
+
+/*
+** State of a parse of a GeoJSON input.
+*/
+typedef struct GeoParse GeoParse;
+struct GeoParse {
+ const unsigned char *z; /* Unparsed input */
+ int nVertex; /* Number of vertexes in a[] */
+ int nAlloc; /* Space allocated to a[] */
+ int nErr; /* Number of errors encountered */
+ GeoCoord *a; /* Array of vertexes. From sqlite3_malloc64() */
+};
+
+/* Do a 4-byte byte swap */
+static void geopolySwab32(unsigned char *a){
+ unsigned char t = a[0];
+ a[0] = a[3];
+ a[3] = t;
+ t = a[1];
+ a[1] = a[2];
+ a[2] = t;
+}
+
+/* Skip whitespace. Return the next non-whitespace character. */
+static char geopolySkipSpace(GeoParse *p){
+ while( p->z[0] && safe_isspace(p->z[0]) ) p->z++;
+ return p->z[0];
+}
+
+/* Parse out a number. Write the value into *pVal if pVal!=0.
+** return non-zero on success and zero if the next token is not a number.
+*/
+static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){
+ char c = geopolySkipSpace(p);
+ const unsigned char *z = p->z;
+ int j = 0;
+ int seenDP = 0;
+ int seenE = 0;
+ if( c=='-' ){
+ j = 1;
+ c = z[j];
+ }
+ if( c=='0' && z[j+1]>='0' && z[j+1]<='9' ) return 0;
+ for(;; j++){
+ c = z[j];
+ if( c>='0' && c<='9' ) continue;
+ if( c=='.' ){
+ if( z[j-1]=='-' ) return 0;
+ if( seenDP ) return 0;
+ seenDP = 1;
+ continue;
+ }
+ if( c=='e' || c=='E' ){
+ if( z[j-1]<'0' ) return 0;
+ if( seenE ) return -1;
+ seenDP = seenE = 1;
+ c = z[j+1];
+ if( c=='+' || c=='-' ){
+ j++;
+ c = z[j+1];
+ }
+ if( c<'0' || c>'9' ) return 0;
+ continue;
+ }
+ break;
+ }
+ if( z[j-1]<'0' ) return 0;
+ if( pVal ) *pVal = atof((const char*)p->z);
+ p->z += j;
+ return 1;
+}
+
+/*
+** If the input is a well-formed JSON array of coordinates with at least
+** four coordinates and where each coordinate is itself a two-value array,
+** then convert the JSON into a GeoPoly object and return a pointer to
+** that object.
+**
+** If any error occurs, return NULL.
+*/
+static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
+ GeoParse s;
+ int rc = SQLITE_OK;
+ memset(&s, 0, sizeof(s));
+ s.z = z;
+ if( geopolySkipSpace(&s)=='[' ){
+ s.z++;
+ while( geopolySkipSpace(&s)=='[' ){
+ int ii = 0;
+ char c;
+ s.z++;
+ if( s.nVertex<=s.nAlloc ){
+ GeoCoord *aNew;
+ s.nAlloc = s.nAlloc*2 + 16;
+ aNew = sqlite3_realloc64(s.a, s.nAlloc*sizeof(GeoCoord)*2 );
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ s.nErr++;
+ break;
+ }
+ s.a = aNew;
+ }
+ while( geopolyParseNumber(&s, ii<=1 ? &s.a[s.nVertex*2+ii] : 0) ){
+ ii++;
+ if( ii==2 ) s.nVertex++;
+ c = geopolySkipSpace(&s);
+ s.z++;
+ if( c==',' ) continue;
+ if( c==']' && ii>=2 ) break;
+ s.nErr++;
+ rc = SQLITE_ERROR;
+ goto parse_json_err;
+ }
+ if( geopolySkipSpace(&s)==',' ){
+ s.z++;
+ continue;
+ }
+ break;
+ }
+ if( geopolySkipSpace(&s)==']'
+ && s.nVertex>=4
+ && s.a[0]==s.a[s.nVertex*2-2]
+ && s.a[1]==s.a[s.nVertex*2-1]
+ && (s.z++, geopolySkipSpace(&s)==0)
+ ){
+ int nByte;
+ GeoPoly *pOut;
+ int x = 1;
+ s.nVertex--; /* Remove the redundant vertex at the end */
+ nByte = sizeof(GeoPoly) * s.nVertex*2*sizeof(GeoCoord);
+ pOut = sqlite3_malloc64( nByte );
+ x = 1;
+ if( pOut==0 ) goto parse_json_err;
+ pOut->nVertex = s.nVertex;
+ memcpy(pOut->a, s.a, s.nVertex*2*sizeof(GeoCoord));
+ pOut->hdr[0] = *(unsigned char*)&x;
+ pOut->hdr[1] = (s.nVertex>>16)&0xff;
+ pOut->hdr[2] = (s.nVertex>>8)&0xff;
+ pOut->hdr[3] = s.nVertex&0xff;
+ sqlite3_free(s.a);
+ if( pRc ) *pRc = SQLITE_OK;
+ return pOut;
+ }else{
+ s.nErr++;
+ rc = SQLITE_ERROR;
+ }
+ }
+parse_json_err:
+ if( pRc ) *pRc = rc;
+ sqlite3_free(s.a);
+ return 0;
+}
+
+/*
+** Given a function parameter, try to interpret it as a polygon, either
+** in the binary format or JSON text. Compute a GeoPoly object and
+** return a pointer to that object. Or if the input is not a well-formed
+** polygon, put an error message in sqlite3_context and return NULL.
+*/
+static GeoPoly *geopolyFuncParam(
+ sqlite3_context *pCtx, /* Context for error messages */
+ sqlite3_value *pVal, /* The value to decode */
+ int *pRc /* Write error here */
+){
+ GeoPoly *p = 0;
+ int nByte;
+ if( sqlite3_value_type(pVal)==SQLITE_BLOB
+ && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
+ ){
+ const unsigned char *a = sqlite3_value_blob(pVal);
+ int nVertex;
+ nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
+ if( (a[0]==0 || a[0]==1)
+ && (nVertex*2*sizeof(GeoCoord) + 4)==nByte
+ ){
+ p = sqlite3_malloc64( sizeof(*p) + (nVertex-1)*2*sizeof(GeoCoord) );
+ if( p==0 ){
+ if( pRc ) *pRc = SQLITE_NOMEM;
+ if( pCtx ) sqlite3_result_error_nomem(pCtx);
+ }else{
+ int x = 1;
+ p->nVertex = nVertex;
+ memcpy(p->hdr, a, nByte);
+ if( a[0] != *(unsigned char*)&x ){
+ int ii;
+ for(ii=0; iia[ii]);
+ }
+ p->hdr[0] ^= 1;
+ }
+ }
+ }
+ if( pRc ) *pRc = SQLITE_OK;
+ return p;
+ }else if( sqlite3_value_type(pVal)==SQLITE_TEXT ){
+ const unsigned char *zJson = sqlite3_value_text(pVal);
+ if( zJson==0 ){
+ if( pRc ) *pRc = SQLITE_NOMEM;
+ return 0;
+ }
+ return geopolyParseJson(zJson, pRc);
+ }else{
+ if( pRc ) *pRc = SQLITE_ERROR;
+ return 0;
+ }
+}
+
+/*
+** Implementation of the geopoly_blob(X) function.
+**
+** If the input is a well-formed Geopoly BLOB or JSON string
+** then return the BLOB representation of the polygon. Otherwise
+** return NULL.
+*/
+static void geopolyBlobFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ if( p ){
+ sqlite3_result_blob(context, p->hdr,
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** SQL function: geopoly_json(X)
+**
+** Interpret X as a polygon and render it as a JSON array
+** of coordinates. Or, if X is not a valid polygon, return NULL.
+*/
+static void geopolyJsonFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ if( p ){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ sqlite3_str *x = sqlite3_str_new(db);
+ int i;
+ sqlite3_str_append(x, "[", 1);
+ for(i=0; inVertex; i++){
+ sqlite3_str_appendf(x, "[%!g,%!g],", p->a[i*2], p->a[i*2+1]);
+ }
+ sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]);
+ sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** SQL function: geopoly_svg(X, ....)
+**
+** Interpret X as a polygon and render it as a SVG .
+** Additional arguments are added as attributes to the .
+*/
+static void geopolySvgFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ if( p ){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ sqlite3_str *x = sqlite3_str_new(db);
+ int i;
+ char cSep = '\'';
+ sqlite3_str_appendf(x, "a[i*2], p->a[i*2+1]);
+ cSep = ' ';
+ }
+ sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]);
+ for(i=1; i");
+ sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** SQL Function: geopoly_xform(poly, A, B, C, D, E, F)
+**
+** Transform and/or translate a polygon as follows:
+**
+** x1 = A*x0 + B*y0 + E
+** y1 = C*x0 + D*y0 + F
+**
+** For a translation:
+**
+** geopoly_xform(poly, 1, 0, 0, 1, x-offset, y-offset)
+**
+** Rotate by R around the point (0,0):
+**
+** geopoly_xform(poly, cos(R), sin(R), -sin(R), cos(R), 0, 0)
+*/
+static void geopolyXformFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ double A = sqlite3_value_double(argv[1]);
+ double B = sqlite3_value_double(argv[2]);
+ double C = sqlite3_value_double(argv[3]);
+ double D = sqlite3_value_double(argv[4]);
+ double E = sqlite3_value_double(argv[5]);
+ double F = sqlite3_value_double(argv[6]);
+ GeoCoord x1, y1, x0, y0;
+ int ii;
+ if( p ){
+ for(ii=0; iinVertex; ii++){
+ x0 = p->a[ii*2];
+ y0 = p->a[ii*2+1];
+ x1 = A*x0 + B*y0 + E;
+ y1 = C*x0 + D*y0 + F;
+ p->a[ii*2] = x1;
+ p->a[ii*2+1] = y1;
+ }
+ sqlite3_result_blob(context, p->hdr,
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Implementation of the geopoly_area(X) function.
+**
+** If the input is a well-formed Geopoly BLOB then return the area
+** enclosed by the polygon. If the polygon circulates clockwise instead
+** of counterclockwise (as it should) then return the negative of the
+** enclosed area. Otherwise return NULL.
+*/
+static void geopolyAreaFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ if( p ){
+ double rArea = 0.0;
+ int ii;
+ for(ii=0; iinVertex-1; ii++){
+ rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */
+ * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */
+ * 0.5;
+ }
+ rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */
+ * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */
+ * 0.5;
+ sqlite3_result_double(context, rArea);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** If pPoly is a polygon, compute its bounding box. Then:
+**
+** (1) if aCoord!=0 store the bounding box in aCoord, returning NULL
+** (2) otherwise, compute a GeoPoly for the bounding box and return the
+** new GeoPoly
+**
+** If pPoly is NULL but aCoord is not NULL, then compute a new GeoPoly from
+** the bounding box in aCoord and return a pointer to that GeoPoly.
+*/
+static GeoPoly *geopolyBBox(
+ sqlite3_context *context, /* For recording the error */
+ sqlite3_value *pPoly, /* The polygon */
+ RtreeCoord *aCoord, /* Results here */
+ int *pRc /* Error code here */
+){
+ GeoPoly *pOut = 0;
+ GeoPoly *p;
+ float mnX, mxX, mnY, mxY;
+ if( pPoly==0 && aCoord!=0 ){
+ p = 0;
+ mnX = aCoord[0].f;
+ mxX = aCoord[1].f;
+ mnY = aCoord[2].f;
+ mxY = aCoord[3].f;
+ goto geopolyBboxFill;
+ }else{
+ p = geopolyFuncParam(context, pPoly, pRc);
+ }
+ if( p ){
+ int ii;
+ mnX = mxX = p->a[0];
+ mnY = mxY = p->a[1];
+ for(ii=1; iinVertex; ii++){
+ double r = p->a[ii*2];
+ if( rmxX ) mxX = r;
+ r = p->a[ii*2+1];
+ if( rmxY ) mxY = r;
+ }
+ if( pRc ) *pRc = SQLITE_OK;
+ if( aCoord==0 ){
+ geopolyBboxFill:
+ pOut = sqlite3_realloc(p, sizeof(GeoPoly)+sizeof(GeoCoord)*6);
+ if( pOut==0 ){
+ sqlite3_free(p);
+ if( context ) sqlite3_result_error_nomem(context);
+ if( pRc ) *pRc = SQLITE_NOMEM;
+ return 0;
+ }
+ pOut->nVertex = 4;
+ ii = 1;
+ pOut->hdr[0] = *(unsigned char*)ⅈ
+ pOut->hdr[1] = 0;
+ pOut->hdr[2] = 0;
+ pOut->hdr[3] = 4;
+ pOut->a[0] = mnX;
+ pOut->a[1] = mnY;
+ pOut->a[2] = mxX;
+ pOut->a[3] = mnY;
+ pOut->a[4] = mxX;
+ pOut->a[5] = mxY;
+ pOut->a[6] = mnX;
+ pOut->a[7] = mxY;
+ }else{
+ sqlite3_free(p);
+ aCoord[0].f = mnX;
+ aCoord[1].f = mxX;
+ aCoord[2].f = mnY;
+ aCoord[3].f = mxY;
+ }
+ }
+ return pOut;
+}
+
+/*
+** Implementation of the geopoly_bbox(X) SQL function.
+*/
+static void geopolyBBoxFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
+ if( p ){
+ sqlite3_result_blob(context, p->hdr,
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** State vector for the geopoly_group_bbox() aggregate function.
+*/
+typedef struct GeoBBox GeoBBox;
+struct GeoBBox {
+ int isInit;
+ RtreeCoord a[4];
+};
+
+
+/*
+** Implementation of the geopoly_group_bbox(X) aggregate SQL function.
+*/
+static void geopolyBBoxStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ RtreeCoord a[4];
+ int rc = SQLITE_OK;
+ (void)geopolyBBox(context, argv[0], a, &rc);
+ if( rc==SQLITE_OK ){
+ GeoBBox *pBBox;
+ pBBox = (GeoBBox*)sqlite3_aggregate_context(context, sizeof(*pBBox));
+ if( pBBox==0 ) return;
+ if( pBBox->isInit==0 ){
+ pBBox->isInit = 1;
+ memcpy(pBBox->a, a, sizeof(RtreeCoord)*4);
+ }else{
+ if( a[0].f < pBBox->a[0].f ) pBBox->a[0] = a[0];
+ if( a[1].f > pBBox->a[1].f ) pBBox->a[1] = a[1];
+ if( a[2].f < pBBox->a[2].f ) pBBox->a[2] = a[2];
+ if( a[3].f > pBBox->a[3].f ) pBBox->a[3] = a[3];
+ }
+ }
+}
+static void geopolyBBoxFinal(
+ sqlite3_context *context
+){
+ GeoPoly *p;
+ GeoBBox *pBBox;
+ pBBox = (GeoBBox*)sqlite3_aggregate_context(context, 0);
+ if( pBBox==0 ) return;
+ p = geopolyBBox(context, 0, pBBox->a, 0);
+ if( p ){
+ sqlite3_result_blob(context, p->hdr,
+ 4+8*p->nVertex, SQLITE_TRANSIENT);
+ sqlite3_free(p);
+ }
+}
+
+
+/*
+** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
+** Returns:
+**
+** +2 x0,y0 is on the line segement
+**
+** +1 x0,y0 is beneath line segment
+**
+** 0 x0,y0 is not on or beneath the line segment or the line segment
+** is vertical and x0,y0 is not on the line segment
+**
+** The left-most coordinate min(x1,x2) is not considered to be part of
+** the line segment for the purposes of this analysis.
+*/
+static int pointBeneathLine(
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2
+){
+ double y;
+ if( x0==x1 && y0==y1 ) return 2;
+ if( x1x2 ) return 0;
+ }else if( x1>x2 ){
+ if( x0<=x2 || x0>x1 ) return 0;
+ }else{
+ /* Vertical line segment */
+ if( x0!=x1 ) return 0;
+ if( y0y1 && y0>y2 ) return 0;
+ return 2;
+ }
+ y = y1 + (y2-y1)*(x0-x1)/(x2-x1);
+ if( y0==y ) return 2;
+ if( y0nVertex-1; ii++){
+ v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
+ p1->a[ii*2+2],p1->a[ii*2+3]);
+ if( v==2 ) break;
+ cnt += v;
+ }
+ if( v!=2 ){
+ v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
+ p1->a[0],p1->a[1]);
+ }
+ if( v==2 ){
+ sqlite3_result_int(context, 1);
+ }else if( ((v+cnt)&1)==0 ){
+ sqlite3_result_int(context, 0);
+ }else{
+ sqlite3_result_int(context, 2);
+ }
+ sqlite3_free(p1);
+}
+
+/* Forward declaration */
+static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2);
+
+/*
+** SQL function: geopoly_within(P1,P2)
+**
+** Return +2 if P1 and P2 are the same polygon
+** Return +1 if P2 is contained within P1
+** Return 0 if any part of P2 is on the outside of P1
+**
+*/
+static void geopolyWithinFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
+ GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ if( p1 && p2 ){
+ int x = geopolyOverlap(p1, p2);
+ if( x<0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ sqlite3_result_int(context, x==2 ? 1 : x==4 ? 2 : 0);
+ }
+ }
+ sqlite3_free(p1);
+ sqlite3_free(p2);
+}
+
+/* Objects used by the overlap algorihm. */
+typedef struct GeoEvent GeoEvent;
+typedef struct GeoSegment GeoSegment;
+typedef struct GeoOverlap GeoOverlap;
+struct GeoEvent {
+ double x; /* X coordinate at which event occurs */
+ int eType; /* 0 for ADD, 1 for REMOVE */
+ GeoSegment *pSeg; /* The segment to be added or removed */
+ GeoEvent *pNext; /* Next event in the sorted list */
+};
+struct GeoSegment {
+ double C, B; /* y = C*x + B */
+ double y; /* Current y value */
+ float y0; /* Initial y value */
+ unsigned char side; /* 1 for p1, 2 for p2 */
+ unsigned int idx; /* Which segment within the side */
+ GeoSegment *pNext; /* Next segment in a list sorted by y */
+};
+struct GeoOverlap {
+ GeoEvent *aEvent; /* Array of all events */
+ GeoSegment *aSegment; /* Array of all segments */
+ int nEvent; /* Number of events */
+ int nSegment; /* Number of segments */
+};
+
+/*
+** Add a single segment and its associated events.
+*/
+static void geopolyAddOneSegment(
+ GeoOverlap *p,
+ GeoCoord x0,
+ GeoCoord y0,
+ GeoCoord x1,
+ GeoCoord y1,
+ unsigned char side,
+ unsigned int idx
+){
+ GeoSegment *pSeg;
+ GeoEvent *pEvent;
+ if( x0==x1 ) return; /* Ignore vertical segments */
+ if( x0>x1 ){
+ GeoCoord t = x0;
+ x0 = x1;
+ x1 = t;
+ t = y0;
+ y0 = y1;
+ y1 = t;
+ }
+ pSeg = p->aSegment + p->nSegment;
+ p->nSegment++;
+ pSeg->C = (y1-y0)/(x1-x0);
+ pSeg->B = y1 - x1*pSeg->C;
+ pSeg->y0 = y0;
+ pSeg->side = side;
+ pSeg->idx = idx;
+ pEvent = p->aEvent + p->nEvent;
+ p->nEvent++;
+ pEvent->x = x0;
+ pEvent->eType = 0;
+ pEvent->pSeg = pSeg;
+ pEvent = p->aEvent + p->nEvent;
+ p->nEvent++;
+ pEvent->x = x1;
+ pEvent->eType = 1;
+ pEvent->pSeg = pSeg;
+}
+
+
+
+/*
+** Insert all segments and events for polygon pPoly.
+*/
+static void geopolyAddSegments(
+ GeoOverlap *p, /* Add segments to this Overlap object */
+ GeoPoly *pPoly, /* Take all segments from this polygon */
+ unsigned char side /* The side of pPoly */
+){
+ unsigned int i;
+ GeoCoord *x;
+ for(i=0; i<(unsigned)pPoly->nVertex-1; i++){
+ x = pPoly->a + (i*2);
+ geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i);
+ }
+ x = pPoly->a + (i*2);
+ geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i);
+}
+
+/*
+** Merge two lists of sorted events by X coordinate
+*/
+static GeoEvent *geopolyEventMerge(GeoEvent *pLeft, GeoEvent *pRight){
+ GeoEvent head, *pLast;
+ head.pNext = 0;
+ pLast = &head;
+ while( pRight && pLeft ){
+ if( pRight->x <= pLeft->x ){
+ pLast->pNext = pRight;
+ pLast = pRight;
+ pRight = pRight->pNext;
+ }else{
+ pLast->pNext = pLeft;
+ pLast = pLeft;
+ pLeft = pLeft->pNext;
+ }
+ }
+ pLast->pNext = pRight ? pRight : pLeft;
+ return head.pNext;
+}
+
+/*
+** Sort an array of nEvent event objects into a list.
+*/
+static GeoEvent *geopolySortEventsByX(GeoEvent *aEvent, int nEvent){
+ int mx = 0;
+ int i, j;
+ GeoEvent *p;
+ GeoEvent *a[50];
+ for(i=0; ipNext = 0;
+ for(j=0; j=mx ) mx = j+1;
+ }
+ p = 0;
+ for(i=0; iy - pLeft->y;
+ if( r==0.0 ) r = pRight->C - pLeft->C;
+ if( r<0.0 ){
+ pLast->pNext = pRight;
+ pLast = pRight;
+ pRight = pRight->pNext;
+ }else{
+ pLast->pNext = pLeft;
+ pLast = pLeft;
+ pLeft = pLeft->pNext;
+ }
+ }
+ pLast->pNext = pRight ? pRight : pLeft;
+ return head.pNext;
+}
+
+/*
+** Sort a list of GeoSegments in order of increasing Y and in the event of
+** a tie, increasing C (slope).
+*/
+static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){
+ int mx = 0;
+ int i;
+ GeoSegment *p;
+ GeoSegment *a[50];
+ while( pList ){
+ p = pList;
+ pList = pList->pNext;
+ p->pNext = 0;
+ for(i=0; i=mx ) mx = i+1;
+ }
+ p = 0;
+ for(i=0; inVertex + p2->nVertex + 2;
+ GeoOverlap *p;
+ int nByte;
+ GeoEvent *pThisEvent;
+ double rX;
+ int rc = 0;
+ int needSort = 0;
+ GeoSegment *pActive = 0;
+ GeoSegment *pSeg;
+ unsigned char aOverlap[4];
+
+ nByte = sizeof(GeoEvent)*nVertex*2
+ + sizeof(GeoSegment)*nVertex
+ + sizeof(GeoOverlap);
+ p = sqlite3_malloc( nByte );
+ if( p==0 ) return -1;
+ p->aEvent = (GeoEvent*)&p[1];
+ p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
+ p->nEvent = p->nSegment = 0;
+ geopolyAddSegments(p, p1, 1);
+ geopolyAddSegments(p, p2, 2);
+ pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
+ rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
+ memset(aOverlap, 0, sizeof(aOverlap));
+ while( pThisEvent ){
+ if( pThisEvent->x!=rX ){
+ GeoSegment *pPrev = 0;
+ int iMask = 0;
+ GEODEBUG(("Distinct X: %g\n", pThisEvent->x));
+ rX = pThisEvent->x;
+ if( needSort ){
+ GEODEBUG(("SORT\n"));
+ pActive = geopolySortSegmentsByYAndC(pActive);
+ needSort = 0;
+ }
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
+ if( pPrev ){
+ if( pPrev->y!=pSeg->y ){
+ GEODEBUG(("MASK: %d\n", iMask));
+ aOverlap[iMask] = 1;
+ }
+ }
+ iMask ^= pSeg->side;
+ pPrev = pSeg;
+ }
+ pPrev = 0;
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
+ double y = pSeg->C*rX + pSeg->B;
+ GEODEBUG(("Segment %d.%d %g->%g\n", pSeg->side, pSeg->idx, pSeg->y, y));
+ pSeg->y = y;
+ if( pPrev ){
+ if( pPrev->y>pSeg->y && pPrev->side!=pSeg->side ){
+ rc = 1;
+ GEODEBUG(("Crossing: %d.%d and %d.%d\n",
+ pPrev->side, pPrev->idx,
+ pSeg->side, pSeg->idx));
+ goto geopolyOverlapDone;
+ }else if( pPrev->y!=pSeg->y ){
+ GEODEBUG(("MASK: %d\n", iMask));
+ aOverlap[iMask] = 1;
+ }
+ }
+ iMask ^= pSeg->side;
+ pPrev = pSeg;
+ }
+ }
+ GEODEBUG(("%s %d.%d C=%g B=%g\n",
+ pThisEvent->eType ? "RM " : "ADD",
+ pThisEvent->pSeg->side, pThisEvent->pSeg->idx,
+ pThisEvent->pSeg->C,
+ pThisEvent->pSeg->B));
+ if( pThisEvent->eType==0 ){
+ /* Add a segment */
+ pSeg = pThisEvent->pSeg;
+ pSeg->y = pSeg->y0;
+ pSeg->pNext = pActive;
+ pActive = pSeg;
+ needSort = 1;
+ }else{
+ /* Remove a segment */
+ if( pActive==pThisEvent->pSeg ){
+ pActive = pActive->pNext;
+ }else{
+ for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
+ if( pSeg->pNext==pThisEvent->pSeg ){
+ pSeg->pNext = pSeg->pNext->pNext;
+ break;
+ }
+ }
+ }
+ }
+ pThisEvent = pThisEvent->pNext;
+ }
+ if( aOverlap[3]==0 ){
+ rc = 0;
+ }else if( aOverlap[1]!=0 && aOverlap[2]==0 ){
+ rc = 3;
+ }else if( aOverlap[1]==0 && aOverlap[2]!=0 ){
+ rc = 2;
+ }else if( aOverlap[1]==0 && aOverlap[2]==0 ){
+ rc = 4;
+ }else{
+ rc = 1;
+ }
+
+geopolyOverlapDone:
+ sqlite3_free(p);
+ return rc;
+}
+
+/*
+** SQL function: geopoly_overlap(P1,P2)
+**
+** Determine whether or not P1 and P2 overlap. Return value:
+**
+** 0 The two polygons are disjoint
+** 1 They overlap
+** 2 P1 is completely contained within P2
+** 3 P2 is completely contained within P1
+** 4 P1 and P2 are the same polygon
+** NULL Either P1 or P2 or both are not valid polygons
+*/
+static void geopolyOverlapFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
+ GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ if( p1 && p2 ){
+ int x = geopolyOverlap(p1, p2);
+ if( x<0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ sqlite3_result_int(context, x);
+ }
+ }
+ sqlite3_free(p1);
+ sqlite3_free(p2);
+}
+
+/*
+** Enable or disable debugging output
+*/
+static void geopolyDebugFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+#ifdef GEOPOLY_ENABLE_DEBUG
+ geo_debug = sqlite3_value_int(argv[0]);
+#endif
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the geopoly virtual table.
+**
+** argv[0] -> module name
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> column names...
+*/
+static int geopolyInit(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* One of the RTREE_COORD_* constants */
+ int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */
+ sqlite3_vtab **ppVtab, /* OUT: New virtual table */
+ char **pzErr, /* OUT: Error message, if any */
+ int isCreate /* True for xCreate, false for xConnect */
+){
+ int rc = SQLITE_OK;
+ Rtree *pRtree;
+ int nDb; /* Length of string argv[1] */
+ int nName; /* Length of string argv[2] */
+ sqlite3_str *pSql;
+ char *zSql;
+ int ii;
+
+ sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+
+ /* Allocate the sqlite3_vtab structure */
+ nDb = (int)strlen(argv[1]);
+ nName = (int)strlen(argv[2]);
+ pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
+ if( !pRtree ){
+ return SQLITE_NOMEM;
+ }
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ pRtree->nBusy = 1;
+ pRtree->base.pModule = &rtreeModule;
+ pRtree->zDb = (char *)&pRtree[1];
+ pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->eCoordType = RTREE_COORD_REAL32;
+ pRtree->nDim = 2;
+ pRtree->nDim2 = 4;
+ memcpy(pRtree->zDb, argv[1], nDb);
+ memcpy(pRtree->zName, argv[2], nName);
+
+
+ /* Create/Connect to the underlying relational database schema. If
+ ** that is successful, call sqlite3_declare_vtab() to configure
+ ** the r-tree table schema.
+ */
+ pSql = sqlite3_str_new(db);
+ sqlite3_str_appendf(pSql, "CREATE TABLE x(_shape");
+ pRtree->nAux = 1; /* Add one for _shape */
+ pRtree->nAuxNotNull = 1; /* The _shape column is always not-null */
+ for(ii=3; iinAux++;
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]);
+ }
+ sqlite3_str_appendf(pSql, ");");
+ zSql = sqlite3_str_finish(pSql);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }
+ sqlite3_free(zSql);
+ if( rc ) goto geopolyInit_fail;
+ pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
+
+ /* Figure out the node size to use. */
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
+ if( rc ) goto geopolyInit_fail;
+ rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
+ if( rc ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ goto geopolyInit_fail;
+ }
+
+ *ppVtab = (sqlite3_vtab *)pRtree;
+ return SQLITE_OK;
+
+geopolyInit_fail:
+ if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
+ assert( *ppVtab==0 );
+ assert( pRtree->nBusy==1 );
+ rtreeRelease(pRtree);
+ return rc;
+}
+
+
+/*
+** GEOPOLY virtual table module xCreate method.
+*/
+static int geopolyCreate(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
+}
+
+/*
+** GEOPOLY virtual table module xConnect method.
+*/
+static int geopolyConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 0);
+}
+
+
+/*
+** GEOPOLY virtual table module xFilter method.
+**
+** Query plans:
+**
+** 1 rowid lookup
+** 2 search for objects overlapping the same bounding box
+** that contains polygon argv[0]
+** 3 search for objects overlapping the same bounding box
+** that contains polygon argv[0]
+** 4 full table scan
+*/
+static int geopolyFilter(
+ sqlite3_vtab_cursor *pVtabCursor, /* The cursor to initialize */
+ int idxNum, /* Query plan */
+ const char *idxStr, /* Not Used */
+ int argc, sqlite3_value **argv /* Parameters to the query plan */
+){
+ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+ RtreeNode *pRoot = 0;
+ int rc = SQLITE_OK;
+ int iCell = 0;
+ sqlite3_stmt *pStmt;
+
+ rtreeReference(pRtree);
+
+ /* Reset the cursor to the same state as rtreeOpen() leaves it in. */
+ freeCursorConstraints(pCsr);
+ sqlite3_free(pCsr->aPoint);
+ pStmt = pCsr->pReadAux;
+ memset(pCsr, 0, sizeof(RtreeCursor));
+ pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
+ pCsr->pReadAux = pStmt;
+
+ pCsr->iStrategy = idxNum;
+ if( idxNum==1 ){
+ /* Special case - lookup by rowid. */
+ RtreeNode *pLeaf; /* Leaf on which the required cell resides */
+ RtreeSearchPoint *p; /* Search point for the leaf */
+ i64 iRowid = sqlite3_value_int64(argv[0]);
+ i64 iNode = 0;
+ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
+ if( rc==SQLITE_OK && pLeaf!=0 ){
+ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
+ assert( p!=0 ); /* Always returns pCsr->sPoint */
+ pCsr->aNode[0] = pLeaf;
+ p->id = iNode;
+ p->eWithin = PARTLY_WITHIN;
+ rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
+ p->iCell = (u8)iCell;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
+ }else{
+ pCsr->atEOF = 1;
+ }
+ }else{
+ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
+ ** with the configured constraints.
+ */
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+ if( rc==SQLITE_OK && idxNum<=3 ){
+ RtreeCoord bbox[4];
+ RtreeConstraint *p;
+ assert( argc==1 );
+ geopolyBBox(0, argv[0], bbox, &rc);
+ if( rc ){
+ goto geopoly_filter_end;
+ }
+ pCsr->aConstraint = p = sqlite3_malloc(sizeof(RtreeConstraint)*4);
+ pCsr->nConstraint = 4;
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*4);
+ memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
+ if( idxNum==2 ){
+ /* Overlap query */
+ p->op = 'B';
+ p->iCoord = 0;
+ p->u.rValue = bbox[1].f;
+ p++;
+ p->op = 'D';
+ p->iCoord = 1;
+ p->u.rValue = bbox[0].f;
+ p++;
+ p->op = 'B';
+ p->iCoord = 2;
+ p->u.rValue = bbox[3].f;
+ p++;
+ p->op = 'D';
+ p->iCoord = 3;
+ p->u.rValue = bbox[2].f;
+ }else{
+ /* Within query */
+ p->op = 'D';
+ p->iCoord = 0;
+ p->u.rValue = bbox[0].f;
+ p++;
+ p->op = 'B';
+ p->iCoord = 1;
+ p->u.rValue = bbox[1].f;
+ p++;
+ p->op = 'D';
+ p->iCoord = 2;
+ p->u.rValue = bbox[2].f;
+ p++;
+ p->op = 'B';
+ p->iCoord = 3;
+ p->u.rValue = bbox[3].f;
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ RtreeSearchPoint *pNew;
+ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ goto geopoly_filter_end;
+ }
+ pNew->id = 1;
+ pNew->iCell = 0;
+ pNew->eWithin = PARTLY_WITHIN;
+ assert( pCsr->bPoint==1 );
+ pCsr->aNode[0] = pRoot;
+ pRoot = 0;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
+ rc = rtreeStepToLeaf(pCsr);
+ }
+ }
+
+geopoly_filter_end:
+ nodeRelease(pRtree, pRoot);
+ rtreeRelease(pRtree);
+ return rc;
+}
+
+/*
+** Rtree virtual table module xBestIndex method. There are three
+** table scan strategies to choose from (in order from most to
+** least desirable):
+**
+** idxNum idxStr Strategy
+** ------------------------------------------------
+** 1 "rowid" Direct lookup by rowid.
+** 2 "rtree" R-tree overlap query using geopoly_overlap()
+** 3 "rtree" R-tree within query using geopoly_within()
+** 4 "fullscan" full-table scan.
+** ------------------------------------------------
+*/
+static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int ii;
+ int iRowidTerm = -1;
+ int iFuncTerm = -1;
+ int idxNum = 0;
+
+ for(ii=0; iinConstraint; ii++){
+ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
+ if( !p->usable ) continue;
+ if( p->iColumn<0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ iRowidTerm = ii;
+ break;
+ }
+ if( p->iColumn==0 && p->op>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
+ /* p->op==SQLITE_INDEX_CONSTRAINT_FUNCTION for geopoly_overlap()
+ ** p->op==(SQLITE_INDEX_CONTRAINT_FUNCTION+1) for geopoly_within().
+ ** See geopolyFindFunction() */
+ iFuncTerm = ii;
+ idxNum = p->op - SQLITE_INDEX_CONSTRAINT_FUNCTION + 2;
+ }
+ }
+
+ if( iRowidTerm>=0 ){
+ pIdxInfo->idxNum = 1;
+ pIdxInfo->idxStr = "rowid";
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
+ pIdxInfo->estimatedCost = 30.0;
+ pIdxInfo->estimatedRows = 1;
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
+ return SQLITE_OK;
+ }
+ if( iFuncTerm>=0 ){
+ pIdxInfo->idxNum = idxNum;
+ pIdxInfo->idxStr = "rtree";
+ pIdxInfo->aConstraintUsage[iFuncTerm].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[iFuncTerm].omit = 0;
+ pIdxInfo->estimatedCost = 300.0;
+ pIdxInfo->estimatedRows = 10;
+ return SQLITE_OK;
+ }
+ pIdxInfo->idxNum = 4;
+ pIdxInfo->idxStr = "fullscan";
+ pIdxInfo->estimatedCost = 3000000.0;
+ pIdxInfo->estimatedRows = 100000;
+ return SQLITE_OK;
+}
+
+
+/*
+** GEOPOLY virtual table module xColumn method.
+*/
+static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+ Rtree *pRtree = (Rtree *)cur->pVtab;
+ RtreeCursor *pCsr = (RtreeCursor *)cur;
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+ int rc = SQLITE_OK;
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+
+ if( rc ) return rc;
+ if( p==0 ) return SQLITE_OK;
+ if( i==0 && sqlite3_vtab_nochange(ctx) ) return SQLITE_OK;
+ if( i<=pRtree->nAux ){
+ if( !pCsr->bAuxValid ){
+ if( pCsr->pReadAux==0 ){
+ rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
+ &pCsr->pReadAux, 0);
+ if( rc ) return rc;
+ }
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
+ nodeGetRowid(pRtree, pNode, p->iCell));
+ rc = sqlite3_step(pCsr->pReadAux);
+ if( rc==SQLITE_ROW ){
+ pCsr->bAuxValid = 1;
+ }else{
+ sqlite3_reset(pCsr->pReadAux);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+ }
+ }
+ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pReadAux, i+2));
+ }
+ return SQLITE_OK;
+}
+
+
+/*
+** The xUpdate method for GEOPOLY module virtual tables.
+**
+** For DELETE:
+**
+** argv[0] = the rowid to be deleted
+**
+** For INSERT:
+**
+** argv[0] = SQL NULL
+** argv[1] = rowid to insert, or an SQL NULL to select automatically
+** argv[2] = _shape column
+** argv[3] = first application-defined column....
+**
+** For UPDATE:
+**
+** argv[0] = rowid to modify. Never NULL
+** argv[1] = rowid after the change. Never NULL
+** argv[2] = new value for _shape
+** argv[3] = new value for first application-defined column....
+*/
+static int geopolyUpdate(
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **aData,
+ sqlite_int64 *pRowid
+){
+ Rtree *pRtree = (Rtree *)pVtab;
+ int rc = SQLITE_OK;
+ RtreeCell cell; /* New cell to insert if nData>1 */
+ i64 oldRowid; /* The old rowid */
+ int oldRowidValid; /* True if oldRowid is valid */
+ i64 newRowid; /* The new rowid */
+ int newRowidValid; /* True if newRowid is valid */
+ int coordChange = 0; /* Change in coordinates */
+
+ if( pRtree->nNodeRef ){
+ /* Unable to write to the btree while another cursor is reading from it,
+ ** since the write might do a rebalance which would disrupt the read
+ ** cursor. */
+ return SQLITE_LOCKED_VTAB;
+ }
+ rtreeReference(pRtree);
+ assert(nData>=1);
+
+ oldRowidValid = sqlite3_value_type(aData[0])!=SQLITE_NULL;;
+ oldRowid = oldRowidValid ? sqlite3_value_int64(aData[0]) : 0;
+ newRowidValid = nData>1 && sqlite3_value_type(aData[1])!=SQLITE_NULL;
+ newRowid = newRowidValid ? sqlite3_value_int64(aData[1]) : 0;
+ cell.iRowid = newRowid;
+
+ if( nData>1 /* not a DELETE */
+ && (!oldRowidValid /* INSERT */
+ || !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
+ || oldRowid!=newRowid) /* Rowid change */
+ ){
+ geopolyBBox(0, aData[2], cell.aCoord, &rc);
+ if( rc ){
+ if( rc==SQLITE_ERROR ){
+ pVtab->zErrMsg =
+ sqlite3_mprintf("_shape does not contain a valid polygon");
+ }
+ goto geopoly_update_end;
+ }
+ coordChange = 1;
+
+ /* If a rowid value was supplied, check if it is already present in
+ ** the table. If so, the constraint has failed. */
+ if( newRowidValid && (!oldRowidValid || oldRowid!=newRowid) ){
+ int steprc;
+ sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
+ steprc = sqlite3_step(pRtree->pReadRowid);
+ rc = sqlite3_reset(pRtree->pReadRowid);
+ if( SQLITE_ROW==steprc ){
+ if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
+ rc = rtreeDeleteRowid(pRtree, cell.iRowid);
+ }else{
+ rc = rtreeConstraintError(pRtree, 0);
+ }
+ }
+ }
+ }
+
+ /* If aData[0] is not an SQL NULL value, it is the rowid of a
+ ** record to delete from the r-tree table. The following block does
+ ** just that.
+ */
+ if( rc==SQLITE_OK && (nData==1 || (coordChange && oldRowidValid)) ){
+ rc = rtreeDeleteRowid(pRtree, oldRowid);
+ }
+
+ /* If the aData[] array contains more than one element, elements
+ ** (aData[2]..aData[argc-1]) contain a new record to insert into
+ ** the r-tree structure.
+ */
+ if( rc==SQLITE_OK && nData>1 && coordChange ){
+ /* Insert the new record into the r-tree */
+ RtreeNode *pLeaf = 0;
+ if( !newRowidValid ){
+ rc = rtreeNewRowid(pRtree, &cell.iRowid);
+ }
+ *pRowid = cell.iRowid;
+ if( rc==SQLITE_OK ){
+ rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf);
+ }
+ if( rc==SQLITE_OK ){
+ int rc2;
+ pRtree->iReinsertHeight = -1;
+ rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
+ rc2 = nodeRelease(pRtree, pLeaf);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ }
+
+ /* Change the data */
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pUp = pRtree->pWriteAux;
+ int jj;
+ int nChange = 0;
+ sqlite3_bind_int64(pUp, 1, cell.iRowid);
+ assert( pRtree->nAux>=1 );
+ if( sqlite3_value_nochange(aData[2]) ){
+ sqlite3_bind_null(pUp, 2);
+ }else{
+ sqlite3_bind_value(pUp, 2, aData[2]);
+ nChange = 1;
+ }
+ for(jj=1; jjnAux; jj++){
+ nChange++;
+ sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
+ }
+ if( nChange ){
+ sqlite3_step(pUp);
+ rc = sqlite3_reset(pUp);
+ }
+ }
+
+geopoly_update_end:
+ rtreeRelease(pRtree);
+ return rc;
+}
+
+/*
+** Report that geopoly_overlap() is an overloaded function suitable
+** for use in xBestIndex.
+*/
+static int geopolyFindFunction(
+ sqlite3_vtab *pVtab,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
+ *pxFunc = geopolyOverlapFunc;
+ *ppArg = 0;
+ return SQLITE_INDEX_CONSTRAINT_FUNCTION;
+ }
+ if( sqlite3_stricmp(zName, "geopoly_within")==0 ){
+ *pxFunc = geopolyWithinFunc;
+ *ppArg = 0;
+ return SQLITE_INDEX_CONSTRAINT_FUNCTION+1;
+ }
+ return 0;
+}
+
+
+static sqlite3_module geopolyModule = {
+ 2, /* iVersion */
+ geopolyCreate, /* xCreate - create a table */
+ geopolyConnect, /* xConnect - connect to an existing table */
+ geopolyBestIndex, /* xBestIndex - Determine search strategy */
+ rtreeDisconnect, /* xDisconnect - Disconnect from a table */
+ rtreeDestroy, /* xDestroy - Drop a table */
+ rtreeOpen, /* xOpen - open a cursor */
+ rtreeClose, /* xClose - close a cursor */
+ geopolyFilter, /* xFilter - configure scan constraints */
+ rtreeNext, /* xNext - advance a cursor */
+ rtreeEof, /* xEof */
+ geopolyColumn, /* xColumn - read data */
+ rtreeRowid, /* xRowid - read data */
+ geopolyUpdate, /* xUpdate - write data */
+ rtreeBeginTransaction, /* xBegin - begin transaction */
+ rtreeEndTransaction, /* xSync - sync transaction */
+ rtreeEndTransaction, /* xCommit - commit transaction */
+ rtreeEndTransaction, /* xRollback - rollback transaction */
+ geopolyFindFunction, /* xFindFunction - function overloading */
+ rtreeRename, /* xRename - rename the table */
+ rtreeSavepoint, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+};
+
+static int sqlite3_geopoly_init(sqlite3 *db){
+ int rc = SQLITE_OK;
+ static const struct {
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ int nArg;
+ const char *zName;
+ } aFunc[] = {
+ { geopolyAreaFunc, 1, "geopoly_area" },
+ { geopolyBlobFunc, 1, "geopoly_blob" },
+ { geopolyJsonFunc, 1, "geopoly_json" },
+ { geopolySvgFunc, -1, "geopoly_svg" },
+ { geopolyWithinFunc, 2, "geopoly_within" },
+ { geopolyContainsPointFunc, 3, "geopoly_contains_point" },
+ { geopolyOverlapFunc, 2, "geopoly_overlap" },
+ { geopolyDebugFunc, 1, "geopoly_debug" },
+ { geopolyBBoxFunc, 1, "geopoly_bbox" },
+ { geopolyXformFunc, 7, "geopoly_xform" },
+ };
+ static const struct {
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
+ void (*xFinal)(sqlite3_context*);
+ const char *zName;
+ } aAgg[] = {
+ { geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox" },
+ };
+ int i;
+ for(i=0; ipWriteRowid, 1);
sqlite3_bind_null(pRtree->pWriteRowid, 2);
sqlite3_step(pRtree->pWriteRowid);
rc = sqlite3_reset(pRtree->pWriteRowid);
@@ -3178,11 +3179,11 @@
/* Insert the new record into the r-tree */
RtreeNode *pLeaf = 0;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
- rc = newRowid(pRtree, &cell.iRowid);
+ rc = rtreeNewRowid(pRtree, &cell.iRowid);
}
*pRowid = cell.iRowid;
if( rc==SQLITE_OK ){
rc = ChooseLeaf(pRtree, &cell, 0, &pLeaf);
@@ -3451,11 +3452,15 @@
int ii;
char *zSql;
sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
for(ii=0; iinAux; ii++){
if( ii ) sqlite3_str_append(p, ",", 1);
- sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
+ if( iinAuxNotNull ){
+ sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii);
+ }else{
+ sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
+ }
}
sqlite3_str_appendf(p, " WHERE rowid=?1");
zSql = sqlite3_str_finish(p);
if( zSql==0 ){
rc = SQLITE_NOMEM;
@@ -4220,10 +4225,14 @@
}
sqlite3_free(zReport);
}
}
+/* Conditionally include the geopoly code */
+#ifdef SQLITE_ENABLE_GEOPOLY
+# include "geopoly.c"
+#endif
/*
** Register the r-tree module with database handle db. This creates the
** virtual table module "rtree" and the debugging/analysis scalar
** function "rtreenode".
@@ -4249,10 +4258,15 @@
}
if( rc==SQLITE_OK ){
void *c = (void *)RTREE_COORD_INT32;
rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0);
}
+#ifdef SQLITE_ENABLE_GEOPOLY
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_geopoly_init(db);
+ }
+#endif
return rc;
}
/*
ADDED ext/rtree/visual01.txt
Index: ext/rtree/visual01.txt
==================================================================
--- /dev/null
+++ ext/rtree/visual01.txt
@@ -0,0 +1,589 @@
+#!sqlite3
+#
+# This is a visual test case for the geopoly virtual table.
+#
+# Run this script in the sqlite3 CLI, and redirect output into an
+# HTML file. This display the HTML in a webbrowser.
+#
+
+/* Test data.
+** Lots of shapes to be displayed over a 1000x800 canvas.
+*/
+CREATE TEMP TABLE basis(name TEXT, jshape TEXT);
+INSERT INTO basis(name,jshape) VALUES
+ ('box-20','[[0,0],[20,0],[20,20],[0,20],[0,0]]'),
+ ('house-70','[[0,0],[50,0],[50,50],[25,70],[0,50],[0,0]]'),
+ ('line-40','[[0,0],[40,0],[40,5],[0,5],[0,0]]'),
+ ('line-80','[[0,0],[80,0],[80,7],[0,7],[0,0]]'),
+ ('arrow-50','[[0,0],[25,25],[0,50],[15,25],[0,0]]'),
+ ('triangle-30','[[0,0],[30,0],[15,30],[0,0]]'),
+ ('angle-30','[[0,0],[30,0],[30,30],[26,30],[26,4],[0,4],[0,0]]'),
+ ('star-10','[[1,0],[5,2],[9,0],[7,4],[10,8],[7,7],[5,10],[3,7],[0,8],[3,4],[1,0]]');
+CREATE TEMP TABLE xform(A,B,C,D,clr);
+INSERT INTO xform(A,B,clr) VALUES
+ (1,0,'black'),
+ (0.707,0.707,'blue'),
+ (0.5,0.866,'red'),
+ (-0.866,0.5,'green');
+CREATE TEMP TABLE xyoff(id1,id2,xoff,yoff,PRIMARY KEY(id1,id2,xoff,yoff))
+ WITHOUT ROWID;
+INSERT INTO xyoff VALUES(1,1,811,659);
+INSERT INTO xyoff VALUES(1,1,235,550);
+INSERT INTO xyoff VALUES(1,1,481,620);
+INSERT INTO xyoff VALUES(1,1,106,494);
+INSERT INTO xyoff VALUES(1,1,487,106);
+INSERT INTO xyoff VALUES(1,1,817,595);
+INSERT INTO xyoff VALUES(1,1,240,504);
+INSERT INTO xyoff VALUES(1,1,806,457);
+INSERT INTO xyoff VALUES(1,1,608,107);
+INSERT INTO xyoff VALUES(1,1,768,662);
+INSERT INTO xyoff VALUES(1,2,808,528);
+INSERT INTO xyoff VALUES(1,2,768,528);
+INSERT INTO xyoff VALUES(1,2,771,171);
+INSERT INTO xyoff VALUES(1,2,275,671);
+INSERT INTO xyoff VALUES(1,2,326,336);
+INSERT INTO xyoff VALUES(1,2,690,688);
+INSERT INTO xyoff VALUES(1,2,597,239);
+INSERT INTO xyoff VALUES(1,2,317,528);
+INSERT INTO xyoff VALUES(1,2,366,223);
+INSERT INTO xyoff VALUES(1,2,621,154);
+INSERT INTO xyoff VALUES(1,3,829,469);
+INSERT INTO xyoff VALUES(1,3,794,322);
+INSERT INTO xyoff VALUES(1,3,358,387);
+INSERT INTO xyoff VALUES(1,3,184,444);
+INSERT INTO xyoff VALUES(1,3,729,500);
+INSERT INTO xyoff VALUES(1,3,333,523);
+INSERT INTO xyoff VALUES(1,3,117,595);
+INSERT INTO xyoff VALUES(1,3,496,201);
+INSERT INTO xyoff VALUES(1,3,818,601);
+INSERT INTO xyoff VALUES(1,3,541,343);
+INSERT INTO xyoff VALUES(1,4,603,248);
+INSERT INTO xyoff VALUES(1,4,761,649);
+INSERT INTO xyoff VALUES(1,4,611,181);
+INSERT INTO xyoff VALUES(1,4,607,233);
+INSERT INTO xyoff VALUES(1,4,860,206);
+INSERT INTO xyoff VALUES(1,4,310,231);
+INSERT INTO xyoff VALUES(1,4,727,539);
+INSERT INTO xyoff VALUES(1,4,660,661);
+INSERT INTO xyoff VALUES(1,4,403,133);
+INSERT INTO xyoff VALUES(1,4,619,331);
+INSERT INTO xyoff VALUES(2,1,712,578);
+INSERT INTO xyoff VALUES(2,1,567,313);
+INSERT INTO xyoff VALUES(2,1,231,423);
+INSERT INTO xyoff VALUES(2,1,490,175);
+INSERT INTO xyoff VALUES(2,1,898,353);
+INSERT INTO xyoff VALUES(2,1,589,483);
+INSERT INTO xyoff VALUES(2,1,188,462);
+INSERT INTO xyoff VALUES(2,1,720,106);
+INSERT INTO xyoff VALUES(2,1,793,380);
+INSERT INTO xyoff VALUES(2,1,154,396);
+INSERT INTO xyoff VALUES(2,2,324,218);
+INSERT INTO xyoff VALUES(2,2,120,327);
+INSERT INTO xyoff VALUES(2,2,655,133);
+INSERT INTO xyoff VALUES(2,2,516,603);
+INSERT INTO xyoff VALUES(2,2,529,572);
+INSERT INTO xyoff VALUES(2,2,481,212);
+INSERT INTO xyoff VALUES(2,2,802,107);
+INSERT INTO xyoff VALUES(2,2,234,509);
+INSERT INTO xyoff VALUES(2,2,501,269);
+INSERT INTO xyoff VALUES(2,2,349,553);
+INSERT INTO xyoff VALUES(2,3,495,685);
+INSERT INTO xyoff VALUES(2,3,897,372);
+INSERT INTO xyoff VALUES(2,3,350,681);
+INSERT INTO xyoff VALUES(2,3,832,257);
+INSERT INTO xyoff VALUES(2,3,778,149);
+INSERT INTO xyoff VALUES(2,3,683,426);
+INSERT INTO xyoff VALUES(2,3,693,217);
+INSERT INTO xyoff VALUES(2,3,746,317);
+INSERT INTO xyoff VALUES(2,3,805,369);
+INSERT INTO xyoff VALUES(2,3,336,585);
+INSERT INTO xyoff VALUES(2,4,890,255);
+INSERT INTO xyoff VALUES(2,4,556,565);
+INSERT INTO xyoff VALUES(2,4,865,555);
+INSERT INTO xyoff VALUES(2,4,230,293);
+INSERT INTO xyoff VALUES(2,4,247,251);
+INSERT INTO xyoff VALUES(2,4,730,563);
+INSERT INTO xyoff VALUES(2,4,318,282);
+INSERT INTO xyoff VALUES(2,4,220,431);
+INSERT INTO xyoff VALUES(2,4,828,336);
+INSERT INTO xyoff VALUES(2,4,278,525);
+INSERT INTO xyoff VALUES(3,1,324,656);
+INSERT INTO xyoff VALUES(3,1,625,362);
+INSERT INTO xyoff VALUES(3,1,155,570);
+INSERT INTO xyoff VALUES(3,1,267,433);
+INSERT INTO xyoff VALUES(3,1,599,121);
+INSERT INTO xyoff VALUES(3,1,873,498);
+INSERT INTO xyoff VALUES(3,1,789,520);
+INSERT INTO xyoff VALUES(3,1,656,378);
+INSERT INTO xyoff VALUES(3,1,831,601);
+INSERT INTO xyoff VALUES(3,1,256,471);
+INSERT INTO xyoff VALUES(3,2,332,258);
+INSERT INTO xyoff VALUES(3,2,305,463);
+INSERT INTO xyoff VALUES(3,2,796,341);
+INSERT INTO xyoff VALUES(3,2,830,229);
+INSERT INTO xyoff VALUES(3,2,413,271);
+INSERT INTO xyoff VALUES(3,2,269,140);
+INSERT INTO xyoff VALUES(3,2,628,441);
+INSERT INTO xyoff VALUES(3,2,747,643);
+INSERT INTO xyoff VALUES(3,2,584,435);
+INSERT INTO xyoff VALUES(3,2,784,314);
+INSERT INTO xyoff VALUES(3,3,722,233);
+INSERT INTO xyoff VALUES(3,3,815,421);
+INSERT INTO xyoff VALUES(3,3,401,267);
+INSERT INTO xyoff VALUES(3,3,451,650);
+INSERT INTO xyoff VALUES(3,3,329,485);
+INSERT INTO xyoff VALUES(3,3,878,370);
+INSERT INTO xyoff VALUES(3,3,162,616);
+INSERT INTO xyoff VALUES(3,3,844,183);
+INSERT INTO xyoff VALUES(3,3,161,216);
+INSERT INTO xyoff VALUES(3,3,176,676);
+INSERT INTO xyoff VALUES(3,4,780,128);
+INSERT INTO xyoff VALUES(3,4,566,121);
+INSERT INTO xyoff VALUES(3,4,646,120);
+INSERT INTO xyoff VALUES(3,4,223,557);
+INSERT INTO xyoff VALUES(3,4,251,117);
+INSERT INTO xyoff VALUES(3,4,139,209);
+INSERT INTO xyoff VALUES(3,4,813,597);
+INSERT INTO xyoff VALUES(3,4,454,538);
+INSERT INTO xyoff VALUES(3,4,616,198);
+INSERT INTO xyoff VALUES(3,4,210,159);
+INSERT INTO xyoff VALUES(4,1,208,415);
+INSERT INTO xyoff VALUES(4,1,326,665);
+INSERT INTO xyoff VALUES(4,1,612,133);
+INSERT INTO xyoff VALUES(4,1,537,513);
+INSERT INTO xyoff VALUES(4,1,638,438);
+INSERT INTO xyoff VALUES(4,1,808,269);
+INSERT INTO xyoff VALUES(4,1,552,121);
+INSERT INTO xyoff VALUES(4,1,100,189);
+INSERT INTO xyoff VALUES(4,1,643,664);
+INSERT INTO xyoff VALUES(4,1,726,378);
+INSERT INTO xyoff VALUES(4,2,478,409);
+INSERT INTO xyoff VALUES(4,2,497,507);
+INSERT INTO xyoff VALUES(4,2,233,148);
+INSERT INTO xyoff VALUES(4,2,587,237);
+INSERT INTO xyoff VALUES(4,2,604,166);
+INSERT INTO xyoff VALUES(4,2,165,455);
+INSERT INTO xyoff VALUES(4,2,320,258);
+INSERT INTO xyoff VALUES(4,2,353,496);
+INSERT INTO xyoff VALUES(4,2,347,495);
+INSERT INTO xyoff VALUES(4,2,166,622);
+INSERT INTO xyoff VALUES(4,3,461,332);
+INSERT INTO xyoff VALUES(4,3,685,278);
+INSERT INTO xyoff VALUES(4,3,427,594);
+INSERT INTO xyoff VALUES(4,3,467,346);
+INSERT INTO xyoff VALUES(4,3,125,548);
+INSERT INTO xyoff VALUES(4,3,597,680);
+INSERT INTO xyoff VALUES(4,3,820,445);
+INSERT INTO xyoff VALUES(4,3,144,330);
+INSERT INTO xyoff VALUES(4,3,557,434);
+INSERT INTO xyoff VALUES(4,3,254,315);
+INSERT INTO xyoff VALUES(4,4,157,339);
+INSERT INTO xyoff VALUES(4,4,249,220);
+INSERT INTO xyoff VALUES(4,4,391,323);
+INSERT INTO xyoff VALUES(4,4,589,429);
+INSERT INTO xyoff VALUES(4,4,859,592);
+INSERT INTO xyoff VALUES(4,4,337,680);
+INSERT INTO xyoff VALUES(4,4,410,288);
+INSERT INTO xyoff VALUES(4,4,636,596);
+INSERT INTO xyoff VALUES(4,4,734,433);
+INSERT INTO xyoff VALUES(4,4,559,549);
+INSERT INTO xyoff VALUES(5,1,549,607);
+INSERT INTO xyoff VALUES(5,1,584,498);
+INSERT INTO xyoff VALUES(5,1,699,116);
+INSERT INTO xyoff VALUES(5,1,525,524);
+INSERT INTO xyoff VALUES(5,1,304,667);
+INSERT INTO xyoff VALUES(5,1,302,232);
+INSERT INTO xyoff VALUES(5,1,403,149);
+INSERT INTO xyoff VALUES(5,1,824,403);
+INSERT INTO xyoff VALUES(5,1,697,203);
+INSERT INTO xyoff VALUES(5,1,293,689);
+INSERT INTO xyoff VALUES(5,2,199,275);
+INSERT INTO xyoff VALUES(5,2,395,393);
+INSERT INTO xyoff VALUES(5,2,657,642);
+INSERT INTO xyoff VALUES(5,2,200,655);
+INSERT INTO xyoff VALUES(5,2,882,234);
+INSERT INTO xyoff VALUES(5,2,483,565);
+INSERT INTO xyoff VALUES(5,2,755,640);
+INSERT INTO xyoff VALUES(5,2,810,305);
+INSERT INTO xyoff VALUES(5,2,731,655);
+INSERT INTO xyoff VALUES(5,2,466,690);
+INSERT INTO xyoff VALUES(5,3,563,584);
+INSERT INTO xyoff VALUES(5,3,491,117);
+INSERT INTO xyoff VALUES(5,3,779,292);
+INSERT INTO xyoff VALUES(5,3,375,637);
+INSERT INTO xyoff VALUES(5,3,253,553);
+INSERT INTO xyoff VALUES(5,3,797,514);
+INSERT INTO xyoff VALUES(5,3,229,480);
+INSERT INTO xyoff VALUES(5,3,257,194);
+INSERT INTO xyoff VALUES(5,3,449,555);
+INSERT INTO xyoff VALUES(5,3,849,630);
+INSERT INTO xyoff VALUES(5,4,329,286);
+INSERT INTO xyoff VALUES(5,4,640,197);
+INSERT INTO xyoff VALUES(5,4,104,150);
+INSERT INTO xyoff VALUES(5,4,438,272);
+INSERT INTO xyoff VALUES(5,4,773,226);
+INSERT INTO xyoff VALUES(5,4,441,650);
+INSERT INTO xyoff VALUES(5,4,242,340);
+INSERT INTO xyoff VALUES(5,4,301,435);
+INSERT INTO xyoff VALUES(5,4,171,397);
+INSERT INTO xyoff VALUES(5,4,541,619);
+INSERT INTO xyoff VALUES(6,1,651,301);
+INSERT INTO xyoff VALUES(6,1,637,137);
+INSERT INTO xyoff VALUES(6,1,765,643);
+INSERT INTO xyoff VALUES(6,1,173,296);
+INSERT INTO xyoff VALUES(6,1,263,192);
+INSERT INTO xyoff VALUES(6,1,791,302);
+INSERT INTO xyoff VALUES(6,1,860,601);
+INSERT INTO xyoff VALUES(6,1,780,445);
+INSERT INTO xyoff VALUES(6,1,462,214);
+INSERT INTO xyoff VALUES(6,1,802,207);
+INSERT INTO xyoff VALUES(6,2,811,685);
+INSERT INTO xyoff VALUES(6,2,533,531);
+INSERT INTO xyoff VALUES(6,2,390,614);
+INSERT INTO xyoff VALUES(6,2,260,580);
+INSERT INTO xyoff VALUES(6,2,116,377);
+INSERT INTO xyoff VALUES(6,2,860,458);
+INSERT INTO xyoff VALUES(6,2,438,590);
+INSERT INTO xyoff VALUES(6,2,604,562);
+INSERT INTO xyoff VALUES(6,2,241,242);
+INSERT INTO xyoff VALUES(6,2,667,298);
+INSERT INTO xyoff VALUES(6,3,787,698);
+INSERT INTO xyoff VALUES(6,3,868,521);
+INSERT INTO xyoff VALUES(6,3,412,587);
+INSERT INTO xyoff VALUES(6,3,640,131);
+INSERT INTO xyoff VALUES(6,3,748,410);
+INSERT INTO xyoff VALUES(6,3,257,244);
+INSERT INTO xyoff VALUES(6,3,411,195);
+INSERT INTO xyoff VALUES(6,3,464,356);
+INSERT INTO xyoff VALUES(6,3,157,339);
+INSERT INTO xyoff VALUES(6,3,434,505);
+INSERT INTO xyoff VALUES(6,4,480,671);
+INSERT INTO xyoff VALUES(6,4,519,228);
+INSERT INTO xyoff VALUES(6,4,404,513);
+INSERT INTO xyoff VALUES(6,4,120,538);
+INSERT INTO xyoff VALUES(6,4,403,663);
+INSERT INTO xyoff VALUES(6,4,477,677);
+INSERT INTO xyoff VALUES(6,4,690,154);
+INSERT INTO xyoff VALUES(6,4,606,498);
+INSERT INTO xyoff VALUES(6,4,430,665);
+INSERT INTO xyoff VALUES(6,4,499,273);
+INSERT INTO xyoff VALUES(7,1,118,526);
+INSERT INTO xyoff VALUES(7,1,817,522);
+INSERT INTO xyoff VALUES(7,1,388,638);
+INSERT INTO xyoff VALUES(7,1,181,265);
+INSERT INTO xyoff VALUES(7,1,442,332);
+INSERT INTO xyoff VALUES(7,1,475,282);
+INSERT INTO xyoff VALUES(7,1,722,633);
+INSERT INTO xyoff VALUES(7,1,104,394);
+INSERT INTO xyoff VALUES(7,1,631,262);
+INSERT INTO xyoff VALUES(7,1,372,392);
+INSERT INTO xyoff VALUES(7,2,600,413);
+INSERT INTO xyoff VALUES(7,2,386,223);
+INSERT INTO xyoff VALUES(7,2,839,174);
+INSERT INTO xyoff VALUES(7,2,293,410);
+INSERT INTO xyoff VALUES(7,2,281,391);
+INSERT INTO xyoff VALUES(7,2,859,387);
+INSERT INTO xyoff VALUES(7,2,478,347);
+INSERT INTO xyoff VALUES(7,2,646,690);
+INSERT INTO xyoff VALUES(7,2,713,234);
+INSERT INTO xyoff VALUES(7,2,199,588);
+INSERT INTO xyoff VALUES(7,3,389,256);
+INSERT INTO xyoff VALUES(7,3,349,542);
+INSERT INTO xyoff VALUES(7,3,363,345);
+INSERT INTO xyoff VALUES(7,3,751,302);
+INSERT INTO xyoff VALUES(7,3,423,386);
+INSERT INTO xyoff VALUES(7,3,267,444);
+INSERT INTO xyoff VALUES(7,3,243,182);
+INSERT INTO xyoff VALUES(7,3,453,658);
+INSERT INTO xyoff VALUES(7,3,126,345);
+INSERT INTO xyoff VALUES(7,3,120,472);
+INSERT INTO xyoff VALUES(7,4,359,654);
+INSERT INTO xyoff VALUES(7,4,339,516);
+INSERT INTO xyoff VALUES(7,4,710,452);
+INSERT INTO xyoff VALUES(7,4,810,560);
+INSERT INTO xyoff VALUES(7,4,644,692);
+INSERT INTO xyoff VALUES(7,4,826,327);
+INSERT INTO xyoff VALUES(7,4,465,462);
+INSERT INTO xyoff VALUES(7,4,310,456);
+INSERT INTO xyoff VALUES(7,4,577,613);
+INSERT INTO xyoff VALUES(7,4,502,555);
+INSERT INTO xyoff VALUES(8,1,601,620);
+INSERT INTO xyoff VALUES(8,1,372,683);
+INSERT INTO xyoff VALUES(8,1,758,399);
+INSERT INTO xyoff VALUES(8,1,485,552);
+INSERT INTO xyoff VALUES(8,1,159,563);
+INSERT INTO xyoff VALUES(8,1,536,303);
+INSERT INTO xyoff VALUES(8,1,122,263);
+INSERT INTO xyoff VALUES(8,1,836,435);
+INSERT INTO xyoff VALUES(8,1,544,146);
+INSERT INTO xyoff VALUES(8,1,270,277);
+INSERT INTO xyoff VALUES(8,2,849,281);
+INSERT INTO xyoff VALUES(8,2,563,242);
+INSERT INTO xyoff VALUES(8,2,704,463);
+INSERT INTO xyoff VALUES(8,2,102,165);
+INSERT INTO xyoff VALUES(8,2,797,524);
+INSERT INTO xyoff VALUES(8,2,612,426);
+INSERT INTO xyoff VALUES(8,2,345,372);
+INSERT INTO xyoff VALUES(8,2,820,376);
+INSERT INTO xyoff VALUES(8,2,789,156);
+INSERT INTO xyoff VALUES(8,2,321,466);
+INSERT INTO xyoff VALUES(8,3,150,332);
+INSERT INTO xyoff VALUES(8,3,136,152);
+INSERT INTO xyoff VALUES(8,3,468,528);
+INSERT INTO xyoff VALUES(8,3,409,192);
+INSERT INTO xyoff VALUES(8,3,820,216);
+INSERT INTO xyoff VALUES(8,3,847,249);
+INSERT INTO xyoff VALUES(8,3,801,267);
+INSERT INTO xyoff VALUES(8,3,181,670);
+INSERT INTO xyoff VALUES(8,3,398,563);
+INSERT INTO xyoff VALUES(8,3,439,576);
+INSERT INTO xyoff VALUES(8,4,123,309);
+INSERT INTO xyoff VALUES(8,4,190,496);
+INSERT INTO xyoff VALUES(8,4,571,531);
+INSERT INTO xyoff VALUES(8,4,290,255);
+INSERT INTO xyoff VALUES(8,4,244,412);
+INSERT INTO xyoff VALUES(8,4,264,596);
+INSERT INTO xyoff VALUES(8,4,253,420);
+INSERT INTO xyoff VALUES(8,4,847,536);
+INSERT INTO xyoff VALUES(8,4,120,288);
+INSERT INTO xyoff VALUES(8,4,331,639);
+
+/* Create the geopoly object from test data above */
+CREATE VIRTUAL TABLE geo1 USING geopoly(type,clr);
+INSERT INTO geo1(_shape,type,clr)
+ SELECT geopoly_xform(jshape,A,B,-B,A,xoff,yoff), basis.name, xform.clr
+ FROM basis, xform, xyoff
+ WHERE xyoff.id1=basis.rowid AND xyoff.id2=xform.rowid;
+
+
+/* Query polygon */
+CREATE TEMP TABLE querypoly(poly JSON, clr TEXT);
+INSERT INTO querypoly(clr, poly) VALUES
+ ('orange', '[[300,300],[400,350],[500,250],[480,500],[400,480],[300,550],[280,450],[320,400],[280,350],[300,300]]');
+
+/* Generate the HTML */
+.print ''
+.print '
Everything
'
+.print ''
+
+.print '
Overlap Query
'
+.print '
'
+.print 'SELECT *'
+.print ' FROM geo1, querypoly'
+.print ' WHERE geopoly_overlap(_shape, poly);'
+.print
+EXPLAIN QUERY PLAN
+SELECT geopoly_svg(_shape,
+ printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
+ )
+ FROM geo1, querypoly
+ WHERE geopoly_overlap(_shape, poly);
+.print '
'
+.print ''
+
+.print '
Overlap Query And Result Bounding Box
'
+.print ''
+
+.print '
Bounding-Box Overlap Query
'
+.print ''
+
+.print '
Within Query
'
+.print '
'
+.print 'SELECT *'
+.print ' FROM geo1, querypoly'
+.print ' WHERE geopoly_within(_shape, poly);'
+.print
+EXPLAIN QUERY PLAN
+SELECT geopoly_svg(_shape,
+ printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
+ )
+ FROM geo1, querypoly
+ WHERE geopoly_within(_shape, poly);
+.print '
'
+.print ''
+
+.print '
Bounding-Box WITHIN Query
'
+.print ''
+
+.print '
Not Overlap Query
'
+.print '
'
+.print 'SELECT *'
+.print ' FROM geo1, querypoly'
+.print ' WHERE NOT geopoly_overlap(_shape, poly);'
+.print
+EXPLAIN QUERY PLAN
+SELECT geopoly_svg(_shape,
+ printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
+ )
+ FROM geo1, querypoly
+ WHERE NOT geopoly_overlap(_shape, poly);
+.print '
'
+.print ''
+
+.print '
Not Within Query
'
+.print '
'
+.print 'SELECT *'
+.print ' FROM geo1, querypoly'
+.print ' WHERE NOT geopoly_within(_shape, poly);'
+.print
+EXPLAIN QUERY PLAN
+SELECT geopoly_svg(_shape,
+ printf('style="fill:none;stroke:%s;stroke-width:1"',geo1.clr)
+ )
+ FROM geo1, querypoly
+ WHERE NOT geopoly_within(_shape, poly);
+.print '
'
+.print ''
+
+.print '
Color-Change For Overlapping Elements
'
+BEGIN;
+UPDATE geo1
+ SET clr=CASE WHEN rowid IN (SELECT geo1.rowid FROM geo1, querypoly
+ WHERE geopoly_overlap(_shape,poly))
+ THEN 'red' ELSE 'blue' END;
+.print ''
+
+.print '
Color-Change And Move Overlapping Elements
'
+BEGIN;
+UPDATE geo1
+ SET clr=CASE WHEN rowid IN (SELECT geo1.rowid FROM geo1, querypoly
+ WHERE geopoly_overlap(_shape,poly))
+ THEN 'red' ELSE '#76ccff' END;
+UPDATE geo1
+ SET _shape=geopoly_xform(_shape,1,0,0,1,300,0)
+ WHERE geopoly_overlap(_shape,(SELECT poly FROM querypoly));
+.print ''
+
+
+.print '
Overlap With Translated Query Polygon
'
+UPDATE querypoly SET poly=geopoly_xform(poly,1,0,0,1,300,0);
+.print ''
+
+.print ''
Index: main.mk
==================================================================
--- main.mk
+++ main.mk
@@ -227,11 +227,12 @@
$(TOP)/ext/icu/sqliteicu.h \
$(TOP)/ext/icu/icu.c
SRC += \
$(TOP)/ext/rtree/sqlite3rtree.h \
$(TOP)/ext/rtree/rtree.h \
- $(TOP)/ext/rtree/rtree.c
+ $(TOP)/ext/rtree/rtree.c \
+ $(TOP)/ext/rtree/geopoly.c
SRC += \
$(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3session.h
SRC += \
$(TOP)/ext/userauth/userauth.c \
@@ -473,11 +474,12 @@
$(TOP)/ext/fts3/fts3.h \
$(TOP)/ext/fts3/fts3Int.h \
$(TOP)/ext/fts3/fts3_hash.h \
$(TOP)/ext/fts3/fts3_tokenizer.h
EXTHDR += \
- $(TOP)/ext/rtree/rtree.h
+ $(TOP)/ext/rtree/rtree.h \
+ $(TOP)/ext/rtree/geopoly.c
EXTHDR += \
$(TOP)/ext/icu/sqliteicu.h
EXTHDR += \
$(TOP)/ext/fts5/fts5Int.h \
fts5parse.h \
Index: src/alter.c
==================================================================
--- src/alter.c
+++ src/alter.c
@@ -18,340 +18,10 @@
** The code in this file only exists if we are not omitting the
** ALTER TABLE logic from the build.
*/
#ifndef SQLITE_OMIT_ALTERTABLE
-
-/*
-** This function is used by SQL generated to implement the
-** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
-** CREATE INDEX command. The second is a table name. The table name in
-** the CREATE TABLE or CREATE INDEX statement is replaced with the third
-** argument and the result returned. Examples:
-**
-** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
-** -> 'CREATE TABLE def(a, b, c)'
-**
-** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
-** -> 'CREATE INDEX i ON def(a, b, c)'
-*/
-static void renameTableFunc(
- sqlite3_context *context,
- int NotUsed,
- sqlite3_value **argv
-){
- unsigned char const *zSql = sqlite3_value_text(argv[0]);
- unsigned char const *zTableName = sqlite3_value_text(argv[1]);
-
- int token;
- Token tname;
- unsigned char const *zCsr = zSql;
- int len = 0;
- char *zRet;
-
- sqlite3 *db = sqlite3_context_db_handle(context);
-
- UNUSED_PARAMETER(NotUsed);
-
- /* The principle used to locate the table name in the CREATE TABLE
- ** statement is that the table name is the first non-space token that
- ** is immediately followed by a TK_LP or TK_USING token.
- */
- if( zSql ){
- do {
- if( !*zCsr ){
- /* Ran out of input before finding an opening bracket. Return NULL. */
- return;
- }
-
- /* Store the token that zCsr points to in tname. */
- tname.z = (char*)zCsr;
- tname.n = len;
-
- /* Advance zCsr to the next token. Store that token type in 'token',
- ** and its length in 'len' (to be used next iteration of this loop).
- */
- do {
- zCsr += len;
- len = sqlite3GetToken(zCsr, &token);
- } while( token==TK_SPACE );
- assert( len>0 || !*zCsr );
- } while( token!=TK_LP && token!=TK_USING );
-
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
- zSql, zTableName, tname.z+tname.n);
- sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
- }
-}
-
-/*
-** This C function implements an SQL user function that is used by SQL code
-** generated by the ALTER TABLE ... RENAME command to modify the definition
-** of any foreign key constraints that use the table being renamed as the
-** parent table. It is passed three arguments:
-**
-** 1) The complete text of the CREATE TABLE statement being modified,
-** 2) The old name of the table being renamed, and
-** 3) The new name of the table being renamed.
-**
-** It returns the new CREATE TABLE statement. For example:
-**
-** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
-** -> 'CREATE TABLE t1(a REFERENCES t3)'
-*/
-#ifndef SQLITE_OMIT_FOREIGN_KEY
-static void renameParentFunc(
- sqlite3_context *context,
- int NotUsed,
- sqlite3_value **argv
-){
- sqlite3 *db = sqlite3_context_db_handle(context);
- char *zOutput = 0;
- char *zResult;
- unsigned char const *zInput = sqlite3_value_text(argv[0]);
- unsigned char const *zOld = sqlite3_value_text(argv[1]);
- unsigned char const *zNew = sqlite3_value_text(argv[2]);
-
- unsigned const char *z; /* Pointer to token */
- int n; /* Length of token z */
- int token; /* Type of token */
-
- UNUSED_PARAMETER(NotUsed);
- if( zInput==0 || zOld==0 ) return;
- for(z=zInput; *z; z=z+n){
- n = sqlite3GetToken(z, &token);
- if( token==TK_REFERENCES ){
- char *zParent;
- do {
- z += n;
- n = sqlite3GetToken(z, &token);
- }while( token==TK_SPACE );
-
- if( token==TK_ILLEGAL ) break;
- zParent = sqlite3DbStrNDup(db, (const char *)z, n);
- if( zParent==0 ) break;
- sqlite3Dequote(zParent);
- if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
- char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
- (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
- );
- sqlite3DbFree(db, zOutput);
- zOutput = zOut;
- zInput = &z[n];
- }
- sqlite3DbFree(db, zParent);
- }
- }
-
- zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput);
- sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC);
- sqlite3DbFree(db, zOutput);
-}
-#endif
-
-#ifndef SQLITE_OMIT_TRIGGER
-/* This function is used by SQL generated to implement the
-** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER
-** statement. The second is a table name. The table name in the CREATE
-** TRIGGER statement is replaced with the third argument and the result
-** returned. This is analagous to renameTableFunc() above, except for CREATE
-** TRIGGER, not CREATE INDEX and CREATE TABLE.
-*/
-static void renameTriggerFunc(
- sqlite3_context *context,
- int NotUsed,
- sqlite3_value **argv
-){
- unsigned char const *zSql = sqlite3_value_text(argv[0]);
- unsigned char const *zTableName = sqlite3_value_text(argv[1]);
-
- int token;
- Token tname;
- int dist = 3;
- unsigned char const *zCsr = zSql;
- int len = 0;
- char *zRet;
- sqlite3 *db = sqlite3_context_db_handle(context);
-
- UNUSED_PARAMETER(NotUsed);
-
- /* The principle used to locate the table name in the CREATE TRIGGER
- ** statement is that the table name is the first token that is immediately
- ** preceded by either TK_ON or TK_DOT and immediately followed by one
- ** of TK_WHEN, TK_BEGIN or TK_FOR.
- */
- if( zSql ){
- do {
-
- if( !*zCsr ){
- /* Ran out of input before finding the table name. Return NULL. */
- return;
- }
-
- /* Store the token that zCsr points to in tname. */
- tname.z = (char*)zCsr;
- tname.n = len;
-
- /* Advance zCsr to the next token. Store that token type in 'token',
- ** and its length in 'len' (to be used next iteration of this loop).
- */
- do {
- zCsr += len;
- len = sqlite3GetToken(zCsr, &token);
- }while( token==TK_SPACE );
- assert( len>0 || !*zCsr );
-
- /* Variable 'dist' stores the number of tokens read since the most
- ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN
- ** token is read and 'dist' equals 2, the condition stated above
- ** to be met.
- **
- ** Note that ON cannot be a database, table or column name, so
- ** there is no need to worry about syntax like
- ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
- */
- dist++;
- if( token==TK_DOT || token==TK_ON ){
- dist = 0;
- }
- } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) );
-
- /* Variable tname now contains the token that is the old table-name
- ** in the CREATE TRIGGER statement.
- */
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
- zSql, zTableName, tname.z+tname.n);
- sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
- }
-}
-#endif /* !SQLITE_OMIT_TRIGGER */
-
-/*
-** This function is used to create the text of expressions of the form:
-**
-** name= OR name= OR ...
-**
-** If argument zWhere is NULL, then a pointer string containing the text
-** "name=" is returned, where is the quoted version
-** of the string passed as argument zConstant. The returned buffer is
-** allocated using sqlite3DbMalloc(). It is the responsibility of the
-** caller to ensure that it is eventually freed.
-**
-** If argument zWhere is not NULL, then the string returned is
-** " OR name=", where is the contents of zWhere.
-** In this case zWhere is passed to sqlite3DbFree() before returning.
-**
-*/
-static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){
- char *zNew;
- if( !zWhere ){
- zNew = sqlite3MPrintf(db, "name=%Q", zConstant);
- }else{
- zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant);
- sqlite3DbFree(db, zWhere);
- }
- return zNew;
-}
-
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-/*
-** Generate the text of a WHERE expression which can be used to select all
-** tables that have foreign key constraints that refer to table pTab (i.e.
-** constraints for which pTab is the parent table) from the sqlite_master
-** table.
-*/
-static char *whereForeignKeys(Parse *pParse, Table *pTab){
- FKey *p;
- char *zWhere = 0;
- for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
- zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName);
- }
- return zWhere;
-}
-#endif
-
-/*
-** Generate the text of a WHERE expression which can be used to select all
-** temporary triggers on table pTab from the sqlite_temp_master table. If
-** table pTab has no temporary triggers, or is itself stored in the
-** temporary database, NULL is returned.
-*/
-static char *whereTempTriggers(Parse *pParse, Table *pTab){
- Trigger *pTrig;
- char *zWhere = 0;
- const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */
-
- /* If the table is not located in the temp-db (in which case NULL is
- ** returned, loop through the tables list of triggers. For each trigger
- ** that is not part of the temp-db schema, add a clause to the WHERE
- ** expression being built up in zWhere.
- */
- if( pTab->pSchema!=pTempSchema ){
- sqlite3 *db = pParse->db;
- for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
- if( pTrig->pSchema==pTempSchema ){
- zWhere = whereOrName(db, zWhere, pTrig->zName);
- }
- }
- }
- if( zWhere ){
- char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere);
- sqlite3DbFree(pParse->db, zWhere);
- zWhere = zNew;
- }
- return zWhere;
-}
-
-/*
-** Generate code to drop and reload the internal representation of table
-** pTab from the database, including triggers and temporary triggers.
-** Argument zName is the name of the table in the database schema at
-** the time the generated code is executed. This can be different from
-** pTab->zName if this function is being called to code part of an
-** "ALTER TABLE RENAME TO" statement.
-*/
-static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
- Vdbe *v;
- char *zWhere;
- int iDb; /* Index of database containing pTab */
-#ifndef SQLITE_OMIT_TRIGGER
- Trigger *pTrig;
-#endif
-
- v = sqlite3GetVdbe(pParse);
- if( NEVER(v==0) ) return;
- assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
- iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- assert( iDb>=0 );
-
-#ifndef SQLITE_OMIT_TRIGGER
- /* Drop any table triggers from the internal schema. */
- for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
- int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
- assert( iTrigDb==iDb || iTrigDb==1 );
- sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0);
- }
-#endif
-
- /* Drop the table and index from the internal schema. */
- sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
-
- /* Reload the table, index and permanent trigger schemas. */
- zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
- if( !zWhere ) return;
- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
-
-#ifndef SQLITE_OMIT_TRIGGER
- /* Now, if the table is not stored in the temp database, reload any temp
- ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
- */
- if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
- sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
- }
-#endif
-}
-
/*
** Parameter zName is the name of a table that is about to be altered
** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
** If the table is a system table, this function leaves an error message
** in pParse->zErr (system tables may not be altered) and returns non-zero.
@@ -363,10 +33,53 @@
sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
return 1;
}
return 0;
}
+
+/*
+** Generate code to verify that the schemas of database zDb and, if
+** bTemp is not true, database "temp", can still be parsed. This is
+** called at the end of the generation of an ALTER TABLE ... RENAME ...
+** statement to ensure that the operation has not rendered any schema
+** objects unusable.
+*/
+void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
+ sqlite3NestedParse(pParse,
+ "SELECT 1 "
+ "FROM \"%w\".%s "
+ "WHERE name NOT LIKE 'sqlite_%%'"
+ " AND sql NOT LIKE 'create virtual%%'"
+ " AND sqlite_rename_test(%Q, sql, type, name, %d)=0 ",
+ zDb, MASTER_NAME,
+ zDb, bTemp
+ );
+
+ if( bTemp==0 ){
+ sqlite3NestedParse(pParse,
+ "SELECT 1 "
+ "FROM temp.%s "
+ "WHERE name NOT LIKE 'sqlite_%%'"
+ " AND sql NOT LIKE 'create virtual%%'"
+ " AND sqlite_rename_test(%Q, sql, type, name, 1)=0 ",
+ MASTER_NAME, zDb
+ );
+ }
+}
+
+/*
+** Generate code to reload the schema for database iDb. And, if iDb!=1, for
+** the temp database as well.
+*/
+void renameReloadSchema(Parse *pParse, int iDb){
+ Vdbe *v = pParse->pVdbe;
+ if( v ){
+ sqlite3ChangeCookie(pParse, iDb);
+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
+ }
+}
/*
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
** command.
*/
@@ -381,13 +94,10 @@
char *zName = 0; /* NULL-terminated version of pName */
sqlite3 *db = pParse->db; /* Database connection */
int nTabName; /* Number of UTF-8 characters in zTabName */
const char *zTabName; /* Original name of the table */
Vdbe *v;
-#ifndef SQLITE_OMIT_TRIGGER
- char *zWhere = 0; /* Where clause to locate temp triggers */
-#endif
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
u32 savedDbFlags; /* Saved value of db->mDbFlags */
savedDbFlags = db->mDbFlags;
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
@@ -456,12 +166,10 @@
*/
v = sqlite3GetVdbe(pParse);
if( v==0 ){
goto exit_rename_table;
}
- sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb);
- sqlite3ChangeCookie(pParse, iDb);
/* If this is a virtual table, invoke the xRename() function if
** one is defined. The xRename() callback will modify the names
** of any resources used by the v-table implementation (including other
** SQLite tables) that are identified by the name of the virtual table.
@@ -477,48 +185,35 @@
/* figure out how many UTF-8 characters are in zName */
zTabName = pTab->zName;
nTabName = sqlite3Utf8CharLen(zTabName, -1);
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- if( db->flags&SQLITE_ForeignKeys ){
- /* If foreign-key support is enabled, rewrite the CREATE TABLE
- ** statements corresponding to all child tables of foreign key constraints
- ** for which the renamed table is the parent table. */
- if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){
- sqlite3NestedParse(pParse,
- "UPDATE \"%w\".%s SET "
- "sql = sqlite_rename_parent(sql, %Q, %Q) "
- "WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere);
- sqlite3DbFree(db, zWhere);
- }
- }
-#endif
-
- /* Modify the sqlite_master table to use the new table name. */
+ /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
+ ** the schema to use the new table name. */
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\".%s SET "
+ "sql = sqlite_rename_table(%Q, sql, %Q, %Q, %d) "
+ "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
+ "AND name NOT LIKE 'sqlite_%%'"
+ , zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
+ );
+
+ /* Update the tbl_name and name columns of the sqlite_master table
+ ** as required. */
sqlite3NestedParse(pParse,
"UPDATE %Q.%s SET "
-#ifdef SQLITE_OMIT_TRIGGER
- "sql = sqlite_rename_table(sql, %Q), "
-#else
- "sql = CASE "
- "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)"
- "ELSE sqlite_rename_table(sql, %Q) END, "
-#endif
"tbl_name = %Q, "
"name = CASE "
"WHEN type='table' THEN %Q "
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END "
"WHERE tbl_name=%Q COLLATE nocase AND "
"(type='table' OR type='index' OR type='trigger');",
- zDb, MASTER_NAME, zName, zName, zName,
-#ifndef SQLITE_OMIT_TRIGGER
- zName,
-#endif
- zName, nTabName, zTabName
+ zDb, MASTER_NAME,
+ zName, zName, zName,
+ nTabName, zTabName
);
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* If the sqlite_sequence table exists in this database, then update
** it with the new table name.
@@ -528,39 +223,25 @@
"UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q",
zDb, zName, pTab->zName);
}
#endif
-#ifndef SQLITE_OMIT_TRIGGER
- /* If there are TEMP triggers on this table, modify the sqlite_temp_master
- ** table. Don't do this if the table being ALTERed is itself located in
- ** the temp database.
- */
- if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
+ /* If the table being renamed is not itself part of the temp database,
+ ** edit view and trigger definitions within the temp database
+ ** as required. */
+ if( iDb!=1 ){
sqlite3NestedParse(pParse,
"UPDATE sqlite_temp_master SET "
- "sql = sqlite_rename_trigger(sql, %Q), "
- "tbl_name = %Q "
- "WHERE %s;", zName, zName, zWhere);
- sqlite3DbFree(db, zWhere);
- }
-#endif
-
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- if( db->flags&SQLITE_ForeignKeys ){
- FKey *p;
- for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
- Table *pFrom = p->pFrom;
- if( pFrom!=pTab ){
- reloadTableSchema(pParse, p->pFrom, pFrom->zName);
- }
- }
- }
-#endif
-
- /* Drop and reload the internal table schema. */
- reloadTableSchema(pParse, pTab, zName);
+ "sql = sqlite_rename_table(%Q, sql, %Q, %Q, 1), "
+ "tbl_name = "
+ "CASE WHEN tbl_name=%Q COLLATE nocase THEN %Q ELSE tbl_name END "
+ "WHERE type IN ('view', 'trigger')"
+ , zDb, zTabName, zName, zTabName, zTabName, zName);
+ }
+
+ renameReloadSchema(pParse, iDb);
+ renameTestSchema(pParse, zDb, iDb==1);
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zName);
db->mDbFlags = savedDbFlags;
@@ -582,16 +263,16 @@
const char *zTab; /* Table name */
char *zCol; /* Null-terminated column definition */
Column *pCol; /* The new column */
Expr *pDflt; /* Default value for the new column */
sqlite3 *db; /* The database connection; */
- Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
+ Vdbe *v; /* The prepared statement under construction */
int r1; /* Temporary registers */
+ char *zWhere; /* WHERE clause for reloading schema */
db = pParse->db;
if( pParse->nErr || db->mallocFailed ) return;
- assert( v!=0 );
pNew = pParse->pNewTable;
assert( pNew );
assert( sqlite3BtreeHoldsAllMutexes(db) );
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
@@ -682,21 +363,24 @@
/* Make sure the schema version is at least 3. But do not upgrade
** from less than 3 to 4, as that will corrupt any preexisting DESC
** index.
*/
- r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
- sqlite3VdbeUsesBtree(v, iDb);
- sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
- sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
- sqlite3ReleaseTempReg(pParse, r1);
-
- /* Reload the schema of the modified table. */
- reloadTableSchema(pParse, pTab, pTab->zName);
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
+ sqlite3VdbeUsesBtree(v, iDb);
+ sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
+ sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
+ sqlite3ReleaseTempReg(pParse, r1);
+ }
+
+ /* Reload the table definition */
+ renameReloadSchema(pParse, iDb);
}
/*
** This function is called by the parser after the table-name in
** an "ALTER TABLE ADD" statement is parsed. Argument
@@ -777,16 +461,10 @@
}
pNew->pSchema = db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
pNew->nTabRef = 1;
- /* Begin a transaction and increment the schema cookie. */
- sqlite3BeginWriteOperation(pParse, 0, iDb);
- v = sqlite3GetVdbe(pParse);
- if( !v ) goto exit_begin_add_column;
- sqlite3ChangeCookie(pParse, iDb);
-
exit_begin_add_column:
sqlite3SrcListDelete(db, pSrc);
return;
}
@@ -891,26 +569,21 @@
zDb, MASTER_NAME,
zDb, pTab->zName, iCol, zNew, bQuote,
pTab->zName
);
- /* Drop and reload the database schema. */
- if( pParse->pVdbe ){
- sqlite3ChangeCookie(pParse, iSchema);
- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iSchema, 0);
- }
-
sqlite3NestedParse(pParse,
- "SELECT 1 "
- "FROM \"%w\".%s "
- "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
- " AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, -1)=0 ",
- zDb, MASTER_NAME,
- pTab->zName,
- zDb, pTab->zName, iCol, zNew
+ "UPDATE temp.%s SET "
+ "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d) "
+ "WHERE type IN ('trigger', 'view')",
+ MASTER_NAME,
+ zDb, pTab->zName, iCol, zNew, bQuote
);
+
+ /* Drop and reload the database schema. */
+ renameReloadSchema(pParse, iSchema);
+ renameTestSchema(pParse, zDb, iSchema==1);
exit_rename_column:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zOld);
sqlite3DbFree(db, zNew);
@@ -950,26 +623,40 @@
int nList; /* Number of tokens in pList */
int iCol; /* Index of column being renamed */
Table *pTab; /* Table being ALTERed */
const char *zOld; /* Old column name */
};
+
+void renameTokenClear(Parse *pParse, void *pPtr){
+ RenameToken *p;
+ assert( pPtr || pParse->db->mallocFailed );
+ for(p=pParse->pRename; p; p=p->pNext){
+ if( p->p==pPtr ){
+ p->p = 0;
+ }
+ }
+}
/*
** Add a new RenameToken object mapping parse tree element pPtr into
** token *pToken to the Parse object currently under construction.
**
** Return a copy of pPtr.
*/
void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
RenameToken *pNew;
+ assert( pPtr || pParse->db->mallocFailed );
+
+ renameTokenClear(pParse, pPtr);
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
if( pNew ){
pNew->p = pPtr;
pNew->t = *pToken;
pNew->pNext = pParse->pRename;
pParse->pRename = pNew;
}
+
return pPtr;
}
/*
** It is assumed that there is already a RenameToken object associated
@@ -976,17 +663,17 @@
** with parse tree element pFrom. This function remaps the associated token
** to parse tree element pTo.
*/
void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){
RenameToken *p;
- for(p=pParse->pRename; ALWAYS(p); p=p->pNext){
+ if( pTo ) renameTokenClear(pParse, pTo);
+ for(p=pParse->pRename; p; p=p->pNext){
if( p->p==pFrom ){
p->p = pTo;
break;
}
}
- assert( pTo==0 || p );
}
/*
** Free the list of RenameToken objects given in the second argument
*/
@@ -1147,10 +834,238 @@
renameTokenFind(pParse, pCtx, (void*)zName);
}
}
}
}
+
+static int renameParseSql(
+ Parse *p,
+ const char *zDb,
+ int bTable,
+ sqlite3 *db,
+ const char *zSql,
+ int bTemp
+){
+ int rc;
+ char *zErr = 0;
+
+ db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
+
+ /* Parse the SQL statement passed as the first argument. If no error
+ ** occurs and the parse does not result in a new table, index or
+ ** trigger object, the database must be corrupt. */
+ memset(p, 0, sizeof(Parse));
+ p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN);
+ p->db = db;
+ p->nQueryLoop = 1;
+ rc = sqlite3RunParser(p, zSql, &zErr);
+ assert( p->zErrMsg==0 );
+ assert( rc!=SQLITE_OK || zErr==0 );
+ assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 );
+ p->zErrMsg = zErr;
+ if( db->mallocFailed ) rc = SQLITE_NOMEM;
+ if( rc==SQLITE_OK
+ && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0
+ ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
+
+#ifdef SQLITE_DEBUG
+ /* Ensure that all mappings in the Parse.pRename list really do map to
+ ** a part of the input string. */
+ if( rc==SQLITE_OK ){
+ int nSql = sqlite3Strlen30(zSql);
+ RenameToken *pToken;
+ for(pToken=p->pRename; pToken; pToken=pToken->pNext){
+ assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
+ }
+ }
+#endif
+
+ db->init.iDb = 0;
+ return rc;
+}
+
+static int renameEditSql(
+ sqlite3_context *pCtx, /* Return result here */
+ RenameCtx *pRename, /* Rename context */
+ const char *zSql, /* SQL statement to edit */
+ const char *zNew, /* New token text */
+ int bQuote /* True to always quote token */
+){
+ int nNew = sqlite3Strlen30(zNew);
+ int nSql = sqlite3Strlen30(zSql);
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+ int rc = SQLITE_OK;
+ char *zQuot;
+ char *zOut;
+ int nQuot;
+
+ /* Set zQuot to point to a buffer containing a quoted copy of the
+ ** identifier zNew. If the corresponding identifier in the original
+ ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
+ ** point to zQuot so that all substitutions are made using the
+ ** quoted version of the new column name. */
+ zQuot = sqlite3_mprintf("\"%w\"", zNew);
+ if( zQuot==0 ){
+ return SQLITE_NOMEM;
+ }else{
+ nQuot = sqlite3Strlen30(zQuot);
+ }
+ if( bQuote ){
+ zNew = zQuot;
+ nNew = nQuot;
+ }
+
+ /* At this point pRename->pList contains a list of RenameToken objects
+ ** corresponding to all tokens in the input SQL that must be replaced
+ ** with the new column name. All that remains is to construct and
+ ** return the edited SQL string. */
+ assert( nQuot>=nNew );
+ zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
+ if( zOut ){
+ int nOut = nSql;
+ memcpy(zOut, zSql, nSql);
+ while( pRename->pList ){
+ int iOff; /* Offset of token to replace in zOut */
+ RenameToken *pBest = renameColumnTokenNext(pRename);
+
+ u32 nReplace;
+ const char *zReplace;
+ if( sqlite3IsIdChar(*pBest->t.z) ){
+ nReplace = nNew;
+ zReplace = zNew;
+ }else{
+ nReplace = nQuot;
+ zReplace = zQuot;
+ }
+
+ iOff = pBest->t.z - zSql;
+ if( pBest->t.n!=nReplace ){
+ memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
+ nOut - (iOff + pBest->t.n)
+ );
+ nOut += nReplace - pBest->t.n;
+ zOut[nOut] = '\0';
+ }
+ memcpy(&zOut[iOff], zReplace, nReplace);
+ sqlite3DbFree(db, pBest);
+ }
+
+ sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, zOut);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ sqlite3_free(zQuot);
+ return rc;
+}
+
+static int renameResolveTrigger(
+ Parse *pParse,
+ const char *zDb
+){
+ sqlite3 *db = pParse->db;
+ TriggerStep *pStep;
+ NameContext sNC;
+ int rc = SQLITE_OK;
+
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ pParse->pTriggerTab = sqlite3FindTable(db, pParse->pNewTrigger->table, zDb);
+ pParse->eTriggerOp = pParse->pNewTrigger->op;
+
+ /* Resolve symbols in WHEN clause */
+ if( pParse->pNewTrigger->pWhen ){
+ rc = sqlite3ResolveExprNames(&sNC, pParse->pNewTrigger->pWhen);
+ }
+
+ for(pStep=pParse->pNewTrigger->step_list;
+ rc==SQLITE_OK && pStep;
+ pStep=pStep->pNext
+ ){
+ if( pStep->pSelect ){
+ sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
+ if( pParse->nErr ) rc = pParse->rc;
+ }
+ if( rc==SQLITE_OK && pStep->zTarget ){
+ Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
+ if( pTarget==0 ){
+ rc = SQLITE_ERROR;
+ }else{
+ SrcList sSrc;
+ memset(&sSrc, 0, sizeof(sSrc));
+ sSrc.nSrc = 1;
+ sSrc.a[0].zName = pStep->zTarget;
+ sSrc.a[0].pTab = pTarget;
+ sNC.pSrcList = &sSrc;
+ if( pStep->pWhere ){
+ rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
+ }
+ assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
+ if( pStep->pUpsert ){
+ Upsert *pUpsert = pStep->pUpsert;
+ assert( rc==SQLITE_OK );
+ pUpsert->pUpsertSrc = &sSrc;
+ sNC.uNC.pUpsert = pUpsert;
+ sNC.ncFlags = NC_UUpsert;
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
+ if( rc==SQLITE_OK ){
+ ExprList *pUpsertSet = pUpsert->pUpsertSet;
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
+ }
+ sNC.ncFlags = 0;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
+ TriggerStep *pStep;
+
+ /* Find tokens to edit in WHEN clause */
+ sqlite3WalkExpr(pWalker, pTrigger->pWhen);
+
+ /* Find tokens to edit in trigger steps */
+ for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
+ sqlite3WalkSelect(pWalker, pStep->pSelect);
+ sqlite3WalkExpr(pWalker, pStep->pWhere);
+ sqlite3WalkExprList(pWalker, pStep->pExprList);
+ if( pStep->pUpsert ){
+ Upsert *pUpsert = pStep->pUpsert;
+ sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget);
+ sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet);
+ sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
+ sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
+ }
+ }
+}
+
+static void renameParseCleanup(Parse *pParse){
+ sqlite3 *db = pParse->db;
+ if( pParse->pVdbe ){
+ sqlite3VdbeFinalize(pParse->pVdbe);
+ }
+ sqlite3DeleteTable(db, pParse->pNewTable);
+ if( pParse->pNewIndex ) sqlite3FreeIndex(db, pParse->pNewIndex);
+ sqlite3DeleteTrigger(db, pParse->pNewTrigger);
+ sqlite3DbFree(db, pParse->zErrMsg);
+ renameTokenFree(db, pParse->pRename);
+ sqlite3ParserReset(pParse);
+}
/*
** SQL function:
**
** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld)
@@ -1160,14 +1075,11 @@
** 2. object: Name of object
** 3. Database: Database name (e.g. "main")
** 4. Table: Table name
** 5. iCol: Index of column to rename
** 6. zNew: New column name
-** 7. bQuote: Non-zero if the new column name should be quoted. Negative
-** if this function is being called to check that the schema
-** can still be parsed and symbols resolved after the column
-** has been renamed.
+** 7. bQuote: Non-zero if the new column name should be quoted.
**
** Do a column rename operation on the CREATE statement given in zSql.
** The iCol-th column (left-most is 0) of table zTable is renamed from zCol
** into zNew. The name should be quoted if bQuote is true.
**
@@ -1196,22 +1108,22 @@
int iCol = sqlite3_value_int(argv[5]);
const char *zNew = (const char*)sqlite3_value_text(argv[6]);
int nNew = sqlite3_value_bytes(argv[6]);
int bQuote = sqlite3_value_int(argv[7]);
const char *zOld;
-
+ int bTemp = 0;
int rc;
char *zErr = 0;
Parse sParse;
Walker sWalker;
Index *pIdx;
char *zOut = 0;
-
- char *zQuot = 0; /* Quoted version of zNew */
- int nQuot = 0; /* Length of zQuot in bytes */
int i;
Table *pTab;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+#endif
UNUSED_PARAMETER(NotUsed);
if( zSql==0 ) return;
if( zTable==0 ) return;
if( zNew==0 ) return;
@@ -1224,58 +1136,14 @@
}
zOld = pTab->aCol[iCol].zName;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
- /* Parse the SQL statement passed as the first argument. If no error
- ** occurs and the parse does not result in a new table, index or
- ** trigger object, the database must be corrupt. */
- memset(&sParse, 0, sizeof(sParse));
- sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
- sParse.db = db;
- sParse.nQueryLoop = 1;
- rc = sqlite3RunParser(&sParse, zSql, &zErr);
- assert( sParse.zErrMsg==0 );
- assert( rc!=SQLITE_OK || zErr==0 );
- assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 );
- sParse.zErrMsg = zErr;
- if( db->mallocFailed ) rc = SQLITE_NOMEM;
- if( rc==SQLITE_OK
- && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0
- ){
- rc = SQLITE_CORRUPT_BKPT;
- }
-
-#ifdef SQLITE_DEBUG
- /* Ensure that all mappings in the Parse.pRename list really do map to
- ** a part of the input string. */
- assert( sqlite3Strlen30(zSql)==nSql );
- if( rc==SQLITE_OK ){
- RenameToken *pToken;
- for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
- assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
- }
- }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = 0;
#endif
-
- /* Set zQuot to point to a buffer containing a quoted copy of the
- ** identifier zNew. If the corresponding identifier in the original
- ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
- ** point to zQuot so that all substitutions are made using the
- ** quoted version of the new column name. */
- if( rc==SQLITE_OK ){
- zQuot = sqlite3_mprintf("\"%w\"", zNew);
- if( zQuot==0 ){
- rc = SQLITE_NOMEM;
- }else{
- nQuot = sqlite3Strlen30(zQuot);
- }
- }
- if( bQuote ){
- zNew = zQuot;
- nNew = nQuot;
- }
+ rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
/* Find tokens that need to be replaced. */
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = &sParse;
sWalker.xExprCallback = renameColumnExprCb;
@@ -1330,180 +1198,273 @@
sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
}else{
/* A trigger */
TriggerStep *pStep;
- NameContext sNC;
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = &sParse;
- sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb);
- sParse.eTriggerOp = sParse.pNewTrigger->op;
-
- /* Resolve symbols in WHEN clause */
- if( sParse.pNewTrigger->pWhen ){
- rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen);
- }
-
- for(pStep=sParse.pNewTrigger->step_list;
- rc==SQLITE_OK && pStep;
- pStep=pStep->pNext
- ){
- if( pStep->pSelect ){
- sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC);
- if( sParse.nErr ) rc = sParse.rc;
- }
- if( rc==SQLITE_OK && pStep->zTarget ){
- Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
- if( pTarget==0 ){
- rc = SQLITE_ERROR;
- }else{
- SrcList sSrc;
- memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = pStep->zTarget;
- sSrc.a[0].pTab = pTarget;
- sNC.pSrcList = &sSrc;
- if( pStep->pWhere ){
- rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
- }
- assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
- if( pStep->pUpsert ){
- Upsert *pUpsert = pStep->pUpsert;
- assert( rc==SQLITE_OK );
- pUpsert->pUpsertSrc = &sSrc;
- sNC.uNC.pUpsert = pUpsert;
- sNC.ncFlags = NC_UUpsert;
- rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
- if( rc==SQLITE_OK ){
- ExprList *pUpsertSet = pUpsert->pUpsertSet;
- if( pTarget==pTab ){
- renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
- }
- rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
- }
- sNC.ncFlags = 0;
- }
-
- if( rc==SQLITE_OK && pTarget==pTab ){
- renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
- renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
- }
- }
- }
- }
-
- if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
+ rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb));
+ if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
+
+ for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
+ if( pStep->zTarget ){
+ Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
+ if( pTarget==pTab ){
+ if( pStep->pUpsert ){
+ ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
+ renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
+ }
+ renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
+ renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
+ }
+ }
+ }
+
/* Find tokens to edit in UPDATE OF clause */
if( sParse.pTriggerTab==pTab ){
renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
}
- /* Find tokens to edit in WHEN clause */
- sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen);
-
- /* Find tokens to edit in trigger steps */
- for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
- sqlite3WalkSelect(&sWalker, pStep->pSelect);
- sqlite3WalkExpr(&sWalker, pStep->pWhere);
- sqlite3WalkExprList(&sWalker, pStep->pExprList);
- if( pStep->pUpsert ){
- Upsert *pUpsert = pStep->pUpsert;
- sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget);
- sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet);
- sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere);
- sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere);
- }
- }
- }
-
- /* At this point sCtx.pList contains a list of RenameToken objects
- ** corresponding to all tokens in the input SQL that must be replaced
- ** with the new column name. All that remains is to construct and
- ** return the edited SQL string. */
- assert( rc==SQLITE_OK );
- assert( nQuot>=nNew );
- zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
- if( zOut ){
- int nOut = nSql;
- memcpy(zOut, zSql, nSql);
- while( sCtx.pList ){
- int iOff; /* Offset of token to replace in zOut */
- RenameToken *pBest = renameColumnTokenNext(&sCtx);
-
- u32 nReplace;
- const char *zReplace;
- if( sqlite3IsIdChar(*pBest->t.z) ){
- nReplace = nNew;
- zReplace = zNew;
- }else{
- nReplace = nQuot;
- zReplace = zQuot;
- }
-
- iOff = pBest->t.z - zSql;
- if( pBest->t.n!=nReplace ){
- memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
- nOut - (iOff + pBest->t.n)
- );
- nOut += nReplace - pBest->t.n;
- zOut[nOut] = '\0';
- }
- memcpy(&zOut[iOff], zReplace, nReplace);
- sqlite3DbFree(db, pBest);
- }
-
- sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
- sqlite3DbFree(db, zOut);
- }else{
- rc = SQLITE_NOMEM;
- }
+ /* Find tokens to edit in various expressions and selects */
+ renameWalkTrigger(&sWalker, sParse.pNewTrigger);
+ }
+
+ assert( rc==SQLITE_OK );
+ rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote);
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
if( sParse.zErrMsg ){
- renameColumnParseError(context, (bQuote<0), argv[1], argv[2], &sParse);
+ renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
}
- if( sParse.pVdbe ){
- sqlite3VdbeFinalize(sParse.pVdbe);
- }
- sqlite3DeleteTable(db, sParse.pNewTable);
- if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
- sqlite3DeleteTrigger(db, sParse.pNewTrigger);
- renameTokenFree(db, sParse.pRename);
+ renameParseCleanup(&sParse);
renameTokenFree(db, sCtx.pList);
- sqlite3DbFree(db, sParse.zErrMsg);
- sqlite3ParserReset(&sParse);
- sqlite3_free(zQuot);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
sqlite3BtreeLeaveAll(db);
}
+
+static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
+ RenameCtx *p = pWalker->u.pRename;
+ if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){
+ renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab);
+ }
+ return WRC_Continue;
+}
+
+/*
+** This is a Walker select callback.
+*/
+static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
+ int i;
+ RenameCtx *p = pWalker->u.pRename;
+ SrcList *pSrc = pSelect->pSrc;
+ for(i=0; inSrc; i++){
+ struct SrcList_item *pItem = &pSrc->a[i];
+ if( pItem->pTab==p->pTab ){
+ renameTokenFind(pWalker->pParse, p, pItem->zName);
+ }
+ }
+
+ return WRC_Continue;
+}
+
+
+/*
+** This C function implements an SQL user function that is used by SQL code
+** generated by the ALTER TABLE ... RENAME command to modify the definition
+** of any foreign key constraints that use the table being renamed as the
+** parent table. It is passed three arguments:
+**
+** 0: The database containing the table being renamed.
+** 1: The complete text of the schema statement being modified,
+** 2: The old name of the table being renamed, and
+** 3: The new name of the table being renamed.
+** 4: True if the schema statement comes from the temp db.
+**
+** It returns the new schema statement. For example:
+**
+** sqlite_rename_table('main', 'CREATE TABLE t1(a REFERENCES t2)','t2','t3',0)
+** -> 'CREATE TABLE t1(a REFERENCES t3)'
+*/
+static void renameTableFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ char *zOutput = 0;
+ char *zResult;
+ unsigned char const *zDb = sqlite3_value_text(argv[0]);
+ unsigned char const *zInput = sqlite3_value_text(argv[1]);
+ unsigned char const *zOld = sqlite3_value_text(argv[2]);
+ unsigned char const *zNew = sqlite3_value_text(argv[3]);
+ int bTemp = sqlite3_value_int(argv[4]);
+
+ if( zInput && zOld && zNew ){
+ unsigned const char *z; /* Pointer to token */
+ int n; /* Length of token z */
+ int token; /* Type of token */
+
+ Parse sParse;
+ int rc;
+ int bQuote = 1;
+ RenameCtx sCtx;
+ Walker sWalker;
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ sqlite3BtreeEnterAll(db);
+
+ memset(&sCtx, 0, sizeof(RenameCtx));
+ sCtx.pTab = sqlite3FindTable(db, zOld, zDb);
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.pParse = &sParse;
+ sWalker.xExprCallback = renameTableExprCb;
+ sWalker.xSelectCallback = renameTableSelectCb;
+ sWalker.u.pRename = &sCtx;
+
+ rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
+
+ if( rc==SQLITE_OK ){
+ if( sParse.pNewTable ){
+ Table *pTab = sParse.pNewTable;
+
+ if( pTab->pSelect ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = &sParse;
+
+ sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
+ if( sParse.nErr ) rc = sParse.rc;
+ sqlite3WalkSelect(&sWalker, pTab->pSelect);
+ }else{
+ /* Modify any FK definitions to point to the new table. */
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+ FKey *pFKey;
+ for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
+ renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
+ }
+ }
+#endif
+
+ /* If this is the table being altered, fix any table refs in CHECK
+ ** expressions. Also update the name that appears right after the
+ ** "CREATE [VIRTUAL] TABLE" bit. */
+ if( sqlite3_stricmp(zOld, pTab->zName)==0 ){
+ sCtx.pTab = pTab;
+ sqlite3WalkExprList(&sWalker, pTab->pCheck);
+ renameTokenFind(&sParse, &sCtx, pTab->zName);
+ }
+ }
+ }
+
+ else if( sParse.pNewIndex ){
+ renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName);
+ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
+ }
+
+#ifndef SQLITE_OMIT_TRIGGER
+ else if( sParse.pNewTrigger ){
+ Trigger *pTrigger = sParse.pNewTrigger;
+ TriggerStep *pStep;
+ if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
+ && sCtx.pTab->pSchema==pTrigger->pTabSchema
+ ){
+ renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
+ }
+
+ rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
+ if( rc==SQLITE_OK ){
+ renameWalkTrigger(&sWalker, pTrigger);
+ for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
+ if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
+ renameTokenFind(&sParse, &sCtx, pStep->zTarget);
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(context, rc);
+ }
+
+ renameParseCleanup(&sParse);
+ renameTokenFree(db, sCtx.pList);
+ sqlite3BtreeLeaveAll(db);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ }
+
+ return;
+}
+
+static void renameTableTest(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ unsigned char const *zDb = sqlite3_value_text(argv[0]);
+ unsigned char const *zInput = sqlite3_value_text(argv[1]);
+ int bTemp = sqlite3_value_int(argv[4]);
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ if( zDb && zInput ){
+ int rc;
+ Parse sParse;
+ rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
+ if( rc==SQLITE_OK ){
+ if( sParse.pNewTable && sParse.pNewTable->pSelect ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = &sParse;
+ sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC);
+ if( sParse.nErr ) rc = sParse.rc;
+ }
+
+ else if( sParse.pNewTrigger ){
+ rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
+ }
+ renameParseCleanup(&sParse);
+ }
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+}
/*
** Register built-in functions used to help implement ALTER TABLE
*/
void sqlite3AlterFunctions(void){
static FuncDef aAlterTableFuncs[] = {
- FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
FUNCTION(sqlite_rename_column, 8, 0, 0, renameColumnFunc),
-#ifndef SQLITE_OMIT_TRIGGER
- FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
-#endif
-#ifndef SQLITE_OMIT_FOREIGN_KEY
- FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
-#endif
+ FUNCTION(sqlite_rename_table, 5, 0, 0, renameTableFunc),
+ FUNCTION(sqlite_rename_test, 5, 0, 0, renameTableTest),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
#endif /* SQLITE_ALTER_TABLE */
Index: src/build.c
==================================================================
--- src/build.c
+++ src/build.c
@@ -781,11 +781,11 @@
if( iDb<0 ){
sqlite3ErrorMsg(pParse, "unknown database %T", pName1);
return -1;
}
}else{
- assert( db->init.iDb==0 || db->init.busy
+ assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT
|| (db->mDbFlags & DBFLAG_Vacuum)!=0);
iDb = db->init.iDb;
*pUnqual = pName1;
}
return iDb;
@@ -876,10 +876,13 @@
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
return;
}
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
zName = sqlite3NameFromToken(db, pName);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, (void*)zName, pName);
+ }
}
pParse->sNameToken = *pName;
if( zName==0 ) return;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto begin_table_error;
@@ -1070,11 +1073,11 @@
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
return;
}
z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
if( z==0 ) return;
- if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
+ if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
memcpy(z, pName->z, pName->n);
z[pName->n] = 0;
sqlite3Dequote(z);
for(i=0; inCol; i++){
if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
@@ -1368,11 +1371,11 @@
if( nTerm==1
&& pCol
&& sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
&& sortOrder!=SQLITE_SO_DESC
){
- if( IN_RENAME_COLUMN && pList ){
+ if( IN_RENAME_OBJECT && pList ){
sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
}
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
@@ -2173,11 +2176,11 @@
/* Make a copy of the entire SELECT statement that defines the view.
** This will force all the Expr.token.z values to be dynamically
** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns.
*/
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
p->pSelect = pSelect;
pSelect = 0;
}else{
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
}
@@ -2746,10 +2749,13 @@
}
pFKey->pFrom = p;
pFKey->pNextFrom = p->pFKey;
z = (char*)&pFKey->aCol[nCol];
pFKey->zTo = z;
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, (void*)z, pTo);
+ }
memcpy(z, pTo->z, pTo->n);
z[pTo->n] = 0;
sqlite3Dequote(z);
z += pTo->n+1;
pFKey->nCol = nCol;
@@ -2768,20 +2774,20 @@
sqlite3ErrorMsg(pParse,
"unknown column \"%s\" in foreign key definition",
pFromCol->a[i].zName);
goto fk_end;
}
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
}
}
}
if( pToCol ){
for(i=0; ia[i].zName);
pFKey->aCol[i].zCol = z;
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
}
memcpy(z, pToCol->a[i].zName, n);
z[n] = 0;
z += n+1;
@@ -3112,11 +3118,11 @@
if( zName==0 ) goto exit_create_index;
assert( pName->z!=0 );
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto exit_create_index;
}
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
if( !db->init.busy ){
if( sqlite3FindTable(db, zName, 0)!=0 ){
sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
goto exit_create_index;
}
@@ -3149,11 +3155,11 @@
}
/* Check for authorization to create an index.
*/
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
const char *zDb = pDb->zDbSName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
goto exit_create_index;
}
i = SQLITE_CREATE_INDEX;
@@ -3237,11 +3243,11 @@
** TODO: Issue a warning if two or more columns of the index are identical.
** TODO: Issue a warning if the table primary key is used as part of the
** index key.
*/
pListItem = pList->a;
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
pIndex->aColExpr = pList;
pList = 0;
}
for(i=0; inKeyCol; i++, pListItem++){
Expr *pCExpr; /* The i-th index expression */
@@ -3397,11 +3403,11 @@
goto exit_create_index;
}
}
}
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
/* Link the new Index structure to its table and to the other
** in-memory database structures.
*/
assert( pParse->nErr==0 );
@@ -3515,11 +3521,11 @@
pIndex->pNext = pOther->pNext;
pOther->pNext = pIndex;
}
pIndex = 0;
}
- else if( IN_RENAME_COLUMN ){
+ else if( IN_RENAME_OBJECT ){
assert( pParse->pNewIndex==0 );
pParse->pNewIndex = pIndex;
pIndex = 0;
}
@@ -3711,11 +3717,11 @@
if( i<0 ){
sqlite3IdListDelete(db, pList);
return 0;
}
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
- if( IN_RENAME_COLUMN && pList->a[i].zName ){
+ if( IN_RENAME_OBJECT && pList->a[i].zName ){
sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
}
return pList;
}
@@ -3960,10 +3966,14 @@
if( p==0 ){
goto append_from_error;
}
assert( p->nSrc>0 );
pItem = &p->a[p->nSrc-1];
+ if( IN_RENAME_OBJECT && pItem->zName ){
+ Token *pToken = (pDatabase && pDatabase->z) ? pDatabase : pTable;
+ sqlite3RenameTokenMap(pParse, pItem->zName, pToken);
+ }
assert( pAlias!=0 );
if( pAlias->n ){
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
pItem->pSelect = pSubquery;
Index: src/expr.c
==================================================================
--- src/expr.c
+++ src/expr.c
@@ -1664,11 +1664,11 @@
assert( pList->nExpr>0 );
pItem = &pList->a[pList->nExpr-1];
assert( pItem->zName==0 );
pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
if( dequote ) sqlite3Dequote(pItem->zName);
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName);
}
}
}
@@ -3739,11 +3739,11 @@
** control overloading) ends up as the second argument to the
** function. The expression "A glob B" is equivalent to
** "glob(B,A). We want to use the A in "A glob B" to test
** for function overloading. But we use the B term in "glob(B,A)".
*/
- if( nFarg>=2 && (pExpr->flags & EP_InfixFunc) ){
+ if( nFarg>=2 && ExprHasProperty(pExpr, EP_InfixFunc) ){
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[1].pExpr);
}else if( nFarg>0 ){
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr);
}
#endif
Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -1327,10 +1327,11 @@
int i, origRc = rc;
for(i=0; i<2 && zName==0; i++, rc &= 0xff){
switch( rc ){
case SQLITE_OK: zName = "SQLITE_OK"; break;
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
Index: src/pager.c
==================================================================
--- src/pager.c
+++ src/pager.c
@@ -995,12 +995,16 @@
** containing the state of the Pager object passed as an argument. This
** is intended to be used within debuggers. For example, as an alternative
** to "print *pPager" in gdb:
**
** (gdb) printf "%s", print_pager_state(pPager)
+**
+** This routine has external linkage in order to suppress compiler warnings
+** about an unused function. It is enclosed within SQLITE_DEBUG and so does
+** not appear in normal builds.
*/
-static char *print_pager_state(Pager *p){
+char *print_pager_state(Pager *p){
static char zRet[1024];
sqlite3_snprintf(1024, zRet,
"Filename: %s\n"
"State: %s errCode=%d\n"
@@ -7275,17 +7279,10 @@
**
** The returned indicate the current (possibly updated) journal-mode.
*/
int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
u8 eOld = pPager->journalMode; /* Prior journalmode */
-
-#ifdef SQLITE_DEBUG
- /* The print_pager_state() routine is intended to be used by the debugger
- ** only. We invoke it once here to suppress a compiler warning. */
- print_pager_state(pPager);
-#endif
-
/* The eMode parameter is always valid */
assert( eMode==PAGER_JOURNALMODE_DELETE
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|| eMode==PAGER_JOURNALMODE_PERSIST
Index: src/parse.y
==================================================================
--- src/parse.y
+++ src/parse.y
@@ -323,11 +323,11 @@
Expr *p = tokenExpr(pParse, TK_STRING, X);
if( p ){
sqlite3ExprIdToTrueFalse(p);
testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) );
}
- sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n);
+ sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n);
}
// In addition to the type name, we also care about the primary key and
// UNIQUE constraints.
//
@@ -681,14 +681,18 @@
dbnm(A) ::= . {A.z=0; A.n=0;}
dbnm(A) ::= DOT nm(X). {A = X;}
%type fullname {SrcList*}
%destructor fullname {sqlite3SrcListDelete(pParse->db, $$);}
-fullname(A) ::= nm(X).
- {A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
-fullname(A) ::= nm(X) DOT nm(Y).
- {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
+fullname(A) ::= nm(X). {
+ A = sqlite3SrcListAppend(pParse->db,0,&X,0);
+ if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X);
+}
+fullname(A) ::= nm(X) DOT nm(Y). {
+ A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);
+ if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y);
+}
%type xfullname {SrcList*}
%destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
xfullname(A) ::= nm(X).
{A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
@@ -952,11 +956,11 @@
sqlite3Dequote(p->u.zToken);
}
#if SQLITE_MAX_EXPR_DEPTH>0
p->nHeight = 1;
#endif
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t);
}
}
return p;
}
@@ -968,19 +972,25 @@
expr(A) ::= id(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/}
expr(A) ::= JOIN_KW(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/}
expr(A) ::= nm(X) DOT nm(Y). {
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
- if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+ sqlite3RenameTokenMap(pParse, (void*)temp1, &X);
+ }
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
- if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
+ sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+ }
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
term(A) ::= STRING(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
term(A) ::= INTEGER(X). {
@@ -1275,10 +1285,13 @@
cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
ON nm(Y) LP sortlist(Z) RP where_opt(W). {
sqlite3CreateIndex(pParse, &X, &D,
sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
&S, W, SQLITE_SO_ASC, NE, SQLITE_IDXTYPE_APPDEF);
+ if( IN_RENAME_OBJECT && pParse->pNewIndex ){
+ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &Y);
+ }
}
%type uniqueflag {int}
uniqueflag(A) ::= UNIQUE. {A = OE_Abort;}
uniqueflag(A) ::= . {A = OE_None;}
@@ -1323,13 +1336,10 @@
){
sqlite3ErrorMsg(pParse, "syntax error after column name \"%.*s\"",
pIdToken->n, pIdToken->z);
}
sqlite3ExprListSetName(pParse, p, pIdToken, 1);
- if( IN_RENAME_COLUMN && p ){
- sqlite3RenameTokenMap(pParse, (void*)(p->a[p->nExpr-1].zName), pIdToken);
- }
return p;
}
} // end %include
eidlist_opt(A) ::= . {A = 0;}
Index: src/pragma.c
==================================================================
--- src/pragma.c
+++ src/pragma.c
@@ -2220,11 +2220,10 @@
sqlite3_str_appendf(&acc, "%c\"%s\"", cSep, pragCName[j]);
cSep = ',';
}
if( i==0 ){
sqlite3_str_appendf(&acc, "(\"%s\"", pPragma->zName);
- cSep = ',';
i++;
}
j = 0;
if( pPragma->mPragFlg & PragFlg_Result1 ){
sqlite3_str_appendall(&acc, ",arg HIDDEN");
Index: src/prepare.c
==================================================================
--- src/prepare.c
+++ src/prepare.c
@@ -91,11 +91,11 @@
db->init.orphanTrigger = 0;
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
- assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 );
+ /* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
pData->rc = rc;
Index: src/resolve.c
==================================================================
--- src/resolve.c
+++ src/resolve.c
@@ -262,10 +262,13 @@
const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
assert( zTabName!=0 );
if( sqlite3StrICmp(zTabName, zTab)!=0 ){
continue;
}
+ if( IN_RENAME_OBJECT && pItem->zAlias ){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab);
+ }
}
if( 0==(cntTab++) ){
pMatch = pItem;
}
for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){
@@ -347,11 +350,11 @@
if( iColnCol ){
cnt++;
#ifndef SQLITE_OMIT_UPSERT
if( pExpr->iTable==2 ){
testcase( iCol==(-1) );
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
pExpr->iColumn = iCol;
pExpr->pTab = pTab;
eNewExprOp = TK_COLUMN;
}else{
pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
@@ -440,11 +443,11 @@
}
resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
}
goto lookupname_end;
}
}
@@ -670,25 +673,29 @@
if( pExpr->op==TK_ID ){
zDb = 0;
zTable = 0;
zColumn = pExpr->u.zToken;
}else{
+ Expr *pLeft = pExpr->pLeft;
notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
pRight = pExpr->pRight;
if( pRight->op==TK_ID ){
zDb = 0;
- zTable = pExpr->pLeft->u.zToken;
}else{
assert( pRight->op==TK_DOT );
- zDb = pExpr->pLeft->u.zToken;
- zTable = pRight->pLeft->u.zToken;
+ zDb = pLeft->u.zToken;
+ pLeft = pRight->pLeft;
pRight = pRight->pRight;
}
+ zTable = pLeft->u.zToken;
zColumn = pRight->u.zToken;
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
}
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft);
+ }
}
return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
}
/* Resolve function names
@@ -767,11 +774,11 @@
notValid(pParse, pNC, "non-deterministic functions",
NC_IdxExpr|NC_PartIdx);
}
}
- if( 0==IN_RENAME_COLUMN ){
+ if( 0==IN_RENAME_OBJECT ){
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
Index: src/rowset.c
==================================================================
--- src/rowset.c
+++ src/rowset.c
@@ -122,43 +122,37 @@
*/
#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */
#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
/*
-** Turn bulk memory into a RowSet object. N bytes of memory
-** are available at pSpace. The db pointer is used as a memory context
-** for any subsequent allocations that need to occur.
-** Return a pointer to the new RowSet object.
-**
-** It must be the case that N is sufficient to make a Rowset. If not
-** an assertion fault occurs.
-**
-** If N is larger than the minimum, use the surplus as an initial
-** allocation of entries available to be filled.
+** Allocate a RowSet object. Return NULL if a memory allocation
+** error occurs.
*/
-RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
- RowSet *p;
- assert( N >= ROUND8(sizeof(*p)) );
- p = pSpace;
- p->pChunk = 0;
- p->db = db;
- p->pEntry = 0;
- p->pLast = 0;
- p->pForest = 0;
- p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
- p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
- p->rsFlags = ROWSET_SORTED;
- p->iBatch = 0;
+RowSet *sqlite3RowSetInit(sqlite3 *db){
+ RowSet *p = sqlite3DbMallocRawNN(db, sizeof(*p));
+ if( p ){
+ int N = sqlite3DbMallocSize(db, p);
+ p->pChunk = 0;
+ p->db = db;
+ p->pEntry = 0;
+ p->pLast = 0;
+ p->pForest = 0;
+ p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
+ p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
+ p->rsFlags = ROWSET_SORTED;
+ p->iBatch = 0;
+ }
return p;
}
/*
** Deallocate all chunks from a RowSet. This frees all memory that
** the RowSet has allocated over its lifetime. This routine is
** the destructor for the RowSet.
*/
-void sqlite3RowSetClear(RowSet *p){
+void sqlite3RowSetClear(void *pArg){
+ RowSet *p = (RowSet*)pArg;
struct RowSetChunk *pChunk, *pNextChunk;
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
pNextChunk = pChunk->pNextChunk;
sqlite3DbFree(p->db, pChunk);
}
@@ -167,10 +161,20 @@
p->pEntry = 0;
p->pLast = 0;
p->pForest = 0;
p->rsFlags = ROWSET_SORTED;
}
+
+/*
+** Deallocate all chunks from a RowSet. This frees all memory that
+** the RowSet has allocated over its lifetime. This routine is
+** the destructor for the RowSet.
+*/
+void sqlite3RowSetDelete(void *pArg){
+ sqlite3RowSetClear(pArg);
+ sqlite3DbFree(((RowSet*)pArg)->db, pArg);
+}
/*
** Allocate a new RowSetEntry object that is associated with the
** given RowSet. Return a pointer to the new and completely uninitialized
** objected.
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -470,10 +470,11 @@
** the most recent error can be obtained using
** [sqlite3_extended_errcode()].
*/
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
+#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8))
#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8))
@@ -6438,10 +6439,11 @@
#define SQLITE_INDEX_CONSTRAINT_NE 68
#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
#define SQLITE_INDEX_CONSTRAINT_IS 72
+#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
/*
** CAPI3REF: Register A Virtual Table Implementation
** METHOD: sqlite3
**
@@ -9050,15 +9052,15 @@
** SQLITE_ERROR is returned if either of these conditions is violated, or
** if schema S does not exist, or if the snapshot object is invalid.
**
** ^A call to sqlite3_snapshot_open() will fail to open if the specified
** snapshot has been overwritten by a [checkpoint]. In this case
-** SQLITE_BUSY_SNAPSHOT is returned.
+** SQLITE_ERROR_SNAPSHOT is returned.
**
** If there is already a read transaction open when this function is
** invoked, then the same read transaction remains open (on the same
-** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT
+** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
** is returned. If another error code - for example SQLITE_PROTOCOL or an
** SQLITE_IOERR error code - is returned, then the final state of the
** read transaction is undefined. If SQLITE_OK is returned, then the
** read transaction is now open on database snapshot P.
**
Index: src/sqliteInt.h
==================================================================
--- src/sqliteInt.h
+++ src/sqliteInt.h
@@ -789,11 +789,12 @@
*/
#ifndef SQLITE_PTRSIZE
# if defined(__SIZEOF_POINTER__)
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
- defined(_M_ARM) || defined(__arm__) || defined(__x86)
+ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
+ (defined(__TOS_AIX__) && !defined(__64BIT__))
# define SQLITE_PTRSIZE 4
# else
# define SQLITE_PTRSIZE 8
# endif
#endif
@@ -830,11 +831,11 @@
*/
#ifndef SQLITE_BYTEORDER
# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
- defined(__arm__)
+ defined(__arm__) || defined(_M_ARM64)
# define SQLITE_BYTEORDER 1234
# elif defined(sparc) || defined(__ppc__)
# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
@@ -3121,10 +3122,11 @@
};
#define PARSE_MODE_NORMAL 0
#define PARSE_MODE_DECLARE_VTAB 1
#define PARSE_MODE_RENAME_COLUMN 2
+#define PARSE_MODE_RENAME_TABLE 3
/*
** Sizes and pointers of various parts of the Parse object.
*/
#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/
@@ -3140,13 +3142,13 @@
#else
#define IN_DECLARE_VTAB (pParse->eParseMode==PARSE_MODE_DECLARE_VTAB)
#endif
#if defined(SQLITE_OMIT_ALTERTABLE)
- #define IN_RENAME_COLUMN 0
+ #define IN_RENAME_OBJECT 0
#else
- #define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN)
+ #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN)
#endif
#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
#define IN_SPECIAL_PARSE 0
#else
@@ -3853,12 +3855,13 @@
u32 sqlite3BitvecSize(Bitvec*);
#ifndef SQLITE_UNTESTABLE
int sqlite3BitvecBuiltinTest(int,int*);
#endif
-RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
-void sqlite3RowSetClear(RowSet*);
+RowSet *sqlite3RowSetInit(sqlite3*);
+void sqlite3RowSetDelete(void*);
+void sqlite3RowSetClear(void*);
void sqlite3RowSetInsert(RowSet*, i64);
int sqlite3RowSetTest(RowSet*, int iBatch, i64);
int sqlite3RowSetNext(RowSet*, i64*);
void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int);
Index: src/tokenize.c
==================================================================
--- src/tokenize.c
+++ src/tokenize.c
@@ -693,11 +693,11 @@
** structure built up in pParse->pNewTable. The calling code (see vtab.c)
** will take responsibility for freeing the Table structure.
*/
sqlite3DeleteTable(db, pParse->pNewTable);
}
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
}
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DbFree(db, pParse->pVList);
Index: src/trigger.c
==================================================================
--- src/trigger.c
+++ src/trigger.c
@@ -179,11 +179,11 @@
zName = sqlite3NameFromToken(db, pName);
if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
@@ -212,11 +212,11 @@
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( !IN_RENAME_COLUMN ){
+ if( !IN_RENAME_OBJECT ){
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int code = SQLITE_CREATE_TRIGGER;
const char *zDb = db->aDb[iTabDb].zDbSName;
const char *zDbTrig = isTemp ? db->aDb[1].zDbSName : zDb;
if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
@@ -246,11 +246,12 @@
pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
pTrigger->pSchema = db->aDb[iDb].pSchema;
pTrigger->pTabSchema = pTab->pSchema;
pTrigger->op = (u8)op;
pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
pTrigger->pWhen = pWhen;
pWhen = 0;
}else{
pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
}
@@ -303,11 +304,11 @@
){
goto triggerfinish_cleanup;
}
#ifndef SQLITE_OMIT_ALTERTABLE
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
assert( !db->init.busy );
pParse->pNewTrigger = pTrig;
pTrig = 0;
}else
#endif
@@ -351,11 +352,11 @@
}
}
triggerfinish_cleanup:
sqlite3DeleteTrigger(db, pTrig);
- assert( IN_RENAME_COLUMN || !pParse->pNewTrigger );
+ assert( IN_RENAME_OBJECT || !pParse->pNewTrigger );
sqlite3DeleteTriggerStep(db, pStepList);
}
/*
** Duplicate a range of text from an SQL statement, then convert all
@@ -398,16 +399,17 @@
** holds both the TriggerStep object and the TriggerStep.target.z string.
**
** If an OOM error occurs, NULL is returned and db->mallocFailed is set.
*/
static TriggerStep *triggerStepAllocate(
- sqlite3 *db, /* Database connection */
+ Parse *pParse, /* Parser context */
u8 op, /* Trigger opcode */
Token *pName, /* The target name */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
){
+ sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
if( pTriggerStep ){
char *z = (char*)&pTriggerStep[1];
@@ -414,10 +416,13 @@
memcpy(z, pName->z, pName->n);
sqlite3Dequote(z);
pTriggerStep->zTarget = z;
pTriggerStep->op = op;
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
+ }
}
return pTriggerStep;
}
/*
@@ -440,13 +445,13 @@
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
assert(pSelect != 0 || db->mallocFailed);
- pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
if( pTriggerStep ){
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
pTriggerStep->pSelect = pSelect;
pSelect = 0;
}else{
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
}
@@ -479,13 +484,13 @@
const char *zEnd /* End of SQL text */
){
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
- pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
if( pTriggerStep ){
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
pTriggerStep->pExprList = pEList;
pTriggerStep->pWhere = pWhere;
pEList = 0;
pWhere = 0;
}else{
@@ -512,13 +517,13 @@
const char *zEnd /* End of SQL text */
){
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
- pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd);
+ pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
if( pTriggerStep ){
- if( IN_RENAME_COLUMN ){
+ if( IN_RENAME_OBJECT ){
pTriggerStep->pWhere = pWhere;
pWhere = 0;
}else{
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
}
Index: src/upsert.c
==================================================================
--- src/upsert.c
+++ src/upsert.c
@@ -202,14 +202,16 @@
int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */
){
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
SrcList *pSrc; /* FROM clause for the UPDATE */
- int iDataCur = pUpsert->iDataCur;
+ int iDataCur;
assert( v!=0 );
+ assert( pUpsert!=0 );
VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
+ iDataCur = pUpsert->iDataCur;
if( pIdx && iCur!=iDataCur ){
if( HasRowid(pTab) ){
int regRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
Index: src/vdbe.c
==================================================================
--- src/vdbe.c
+++ src/vdbe.c
@@ -500,11 +500,11 @@
printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
printf(" r:%g", p->u.r);
#endif
- }else if( p->flags & MEM_RowSet ){
+ }else if( sqlite3VdbeMemIsRowSet(p) ){
printf(" (rowset)");
}else{
char zBuf[200];
sqlite3VdbeMemPrettyPrint(p, zBuf);
printf(" %s", zBuf);
@@ -5912,15 +5912,15 @@
*/
case OP_RowSetAdd: { /* in1, in2 */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
assert( (pIn2->flags & MEM_Int)!=0 );
- if( (pIn1->flags & MEM_RowSet)==0 ){
- sqlite3VdbeMemSetRowSet(pIn1);
- if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
+ if( (pIn1->flags & MEM_Blob)==0 ){
+ if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
}
- sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i);
+ assert( sqlite3VdbeMemIsRowSet(pIn1) );
+ sqlite3RowSetInsert((RowSet*)pIn1->z, pIn2->u.i);
break;
}
/* Opcode: RowSetRead P1 P2 P3 * *
** Synopsis: r[P3]=rowset(P1)
@@ -5932,12 +5932,13 @@
*/
case OP_RowSetRead: { /* jump, in1, out3 */
i64 val;
pIn1 = &aMem[pOp->p1];
- if( (pIn1->flags & MEM_RowSet)==0
- || sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0
+ assert( (pIn1->flags & MEM_Blob)==0 || sqlite3VdbeMemIsRowSet(pIn1) );
+ if( (pIn1->flags & MEM_Blob)==0
+ || sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0
){
/* The boolean index is empty */
sqlite3VdbeMemSetNull(pIn1);
VdbeBranchTaken(1,2);
goto jump_to_p2_and_check_for_interrupt;
@@ -5982,24 +5983,23 @@
assert( pIn3->flags&MEM_Int );
/* If there is anything other than a rowset object in memory cell P1,
** delete it now and initialize P1 with an empty rowset
*/
- if( (pIn1->flags & MEM_RowSet)==0 ){
- sqlite3VdbeMemSetRowSet(pIn1);
- if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
+ if( (pIn1->flags & MEM_Blob)==0 ){
+ if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
}
-
+ assert( sqlite3VdbeMemIsRowSet(pIn1) );
assert( pOp->p4type==P4_INT32 );
assert( iSet==-1 || iSet>=0 );
if( iSet ){
- exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i);
+ exists = sqlite3RowSetTest((RowSet*)pIn1->z, iSet, pIn3->u.i);
VdbeBranchTaken(exists!=0,2);
if( exists ) goto jump_to_p2;
}
if( iSet>=0 ){
- sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
+ sqlite3RowSetInsert((RowSet*)pIn1->z, pIn3->u.i);
}
break;
}
@@ -6059,11 +6059,11 @@
/* Register pRt is used to store the memory required to save the state
** of the current program, and the memory required at runtime to execute
** the trigger program. If this trigger has been fired before, then pRt
** is already allocated. Otherwise, it must be initialized. */
- if( (pRt->flags&MEM_Frame)==0 ){
+ if( (pRt->flags&MEM_Blob)==0 ){
/* SubProgram.nMem is set to the number of memory cells used by the
** program stored in SubProgram.aOp. As well as these, one memory
** cell is required for each cursor used by the program. Set local
** variable nMem (and later, VdbeFrame.nChildMem) to this value.
*/
@@ -6077,12 +6077,14 @@
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
}
sqlite3VdbeMemRelease(pRt);
- pRt->flags = MEM_Frame;
- pRt->u.pFrame = pFrame;
+ pRt->flags = MEM_Blob|MEM_Dyn;
+ pRt->z = (char*)pFrame;
+ pRt->n = nByte;
+ pRt->xDel = sqlite3VdbeFrameMemDel;
pFrame->v = p;
pFrame->nChildMem = nMem;
pFrame->nChildCsr = pProgram->nCsr;
pFrame->pc = (int)(pOp - aOp);
@@ -6094,18 +6096,22 @@
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
pFrame->anExec = p->anExec;
#endif
+#ifdef SQLITE_DEBUG
+ pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
+#endif
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
pMem->flags = MEM_Undefined;
pMem->db = db;
}
}else{
- pFrame = pRt->u.pFrame;
+ pFrame = (VdbeFrame*)pRt->z;
+ assert( pRt->xDel==sqlite3VdbeFrameMemDel );
assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
|| (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) );
assert( pProgram->nCsr==pFrame->nChildCsr );
assert( (int)(pOp - aOp)==pFrame->pc );
}
Index: src/vdbeInt.h
==================================================================
--- src/vdbeInt.h
+++ src/vdbeInt.h
@@ -167,10 +167,13 @@
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u8 *aOnce; /* Bitmask used by OP_Once */
void *token; /* Copy of SubProgram.token */
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
AuxData *pAuxData; /* Linked list of auxdata allocations */
+#if SQLITE_DEBUG
+ u32 iFrameMagic; /* magic number for sanity checking */
+#endif
int nCursor; /* Number of entries in apCsr */
int pc; /* Program Counter in parent (calling) frame */
int nOp; /* Size of aOp array */
int nMem; /* Number of entries in aMem */
int nChildMem; /* Number of memory cells for child frame */
@@ -177,10 +180,17 @@
int nChildCsr; /* Number of cursors for child frame */
int nChange; /* Statement changes (Vdbe.nChange) */
int nDbChange; /* Value of db->nChange */
};
+/* Magic number for sanity checking on VdbeFrame objects */
+#define SQLITE_FRAME_MAGIC 0x879fb71e
+
+/*
+** Return a pointer to the array of registers allocated for use
+** by a VdbeFrame.
+*/
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
/*
** Internally, the vdbe manipulates nearly all SQL values as Mem
** structures. Each Mem struct may cache multiple representations (string,
@@ -191,12 +201,10 @@
double r; /* Real value used when MEM_Real is set in flags */
i64 i; /* Integer value used when MEM_Int is set in flags */
int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
- RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
- VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u;
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
u8 eSubtype; /* Subtype for this value */
int n; /* Number of characters in string value, excluding '\0' */
@@ -236,12 +244,12 @@
#define MEM_Str 0x0002 /* Value is a string */
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_AffMask 0x001f /* Mask of affinity bits */
-#define MEM_RowSet 0x0020 /* Value is a RowSet object */
-#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
+/* Available 0x0020 */
+/* Available 0x0040 */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
#define MEM_TypeMask 0xc1ff /* Mask of type bits */
@@ -264,11 +272,11 @@
/* Return TRUE if Mem X contains dynamically allocated content - anything
** that needs to be deallocated to avoid a leak.
*/
#define VdbeMemDynamic(X) \
- (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
+ (((X)->flags&(MEM_Agg|MEM_Dyn))!=0)
/*
** Clear any existing type flags from a Mem and replace them with f
*/
#define MemSetTypeFlag(p, f) \
@@ -477,11 +485,14 @@
#endif
void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*));
void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
void sqlite3VdbeMemSetNull(Mem*);
void sqlite3VdbeMemSetZeroBlob(Mem*,int);
-void sqlite3VdbeMemSetRowSet(Mem*);
+#ifdef SQLITE_DEBUG
+int sqlite3VdbeMemIsRowSet(const Mem*);
+#endif
+int sqlite3VdbeMemSetRowSet(Mem*);
int sqlite3VdbeMemMakeWriteable(Mem*);
int sqlite3VdbeMemStringify(Mem*, u8, u8);
i64 sqlite3VdbeIntValue(Mem*);
int sqlite3VdbeMemIntegerify(Mem*);
double sqlite3VdbeRealValue(Mem*);
@@ -498,11 +509,15 @@
#endif
const char *sqlite3OpcodeName(int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
int sqlite3VdbeCloseStatement(Vdbe *, int);
-void sqlite3VdbeFrameDelete(VdbeFrame*);
+#ifdef SQLITE_DEBUG
+int sqlite3VdbeFrameIsValid(VdbeFrame*);
+#endif
+void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */
+void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
int sqlite3VdbeFrameRestore(VdbeFrame *);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
#endif
int sqlite3VdbeTransferError(Vdbe *p);
Index: src/vdbeaux.c
==================================================================
--- src/vdbeaux.c
+++ src/vdbeaux.c
@@ -314,11 +314,11 @@
** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/
void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
if( pParse->explain==2 ){
char *zMsg;
- Vdbe *v = pParse->pVdbe;
+ Vdbe *v;
va_list ap;
int iThis;
va_start(ap, zFmt);
zMsg = sqlite3VMPrintf(pParse->db, zFmt, ap);
va_end(ap);
@@ -1659,13 +1659,13 @@
** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
testcase( p->flags & MEM_Agg );
testcase( p->flags & MEM_Dyn );
- testcase( p->flags & MEM_Frame );
+ testcase( p->xDel==sqlite3VdbeFrameMemDel );
testcase( p->flags & MEM_RowSet );
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
+ if( p->flags&(MEM_Agg|MEM_Dyn) ){
sqlite3VdbeMemRelease(p);
}else if( p->szMalloc ){
sqlite3DbFreeNN(db, p->zMalloc);
p->szMalloc = 0;
}
@@ -1672,19 +1672,49 @@
p->flags = MEM_Undefined;
}while( (++p)iFrameMagic!=SQLITE_FRAME_MAGIC ) return 0;
+ return 1;
+}
+#endif
+
+
+/*
+** This is a destructor on a Mem object (which is really an sqlite3_value)
+** that deletes the Frame object that is attached to it as a blob.
+**
+** This routine does not delete the Frame right away. It merely adds the
+** frame to a list of frames to be deleted when the Vdbe halts.
+*/
+void sqlite3VdbeFrameMemDel(void *pArg){
+ VdbeFrame *pFrame = (VdbeFrame*)pArg;
+ assert( sqlite3VdbeFrameIsValid(pFrame) );
+ pFrame->pParent = pFrame->v->pDelFrame;
+ pFrame->v->pDelFrame = pFrame;
+}
+
/*
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
** allocated by the OP_Program opcode in sqlite3VdbeExec().
*/
void sqlite3VdbeFrameDelete(VdbeFrame *p){
int i;
Mem *aMem = VdbeFrameMem(p);
VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem];
+ assert( sqlite3VdbeFrameIsValid(p) );
for(i=0; inChildCsr; i++){
sqlite3VdbeFreeCursor(p->v, apCsr[i]);
}
releaseMemArray(aMem, p->nChildMem);
sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0);
@@ -3959,11 +3989,11 @@
int combined_flags;
f1 = pMem1->flags;
f2 = pMem2->flags;
combined_flags = f1|f2;
- assert( (combined_flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem1) && !sqlite3VdbeMemIsRowSet(pMem2) );
/* If one value is NULL, it is less than the other. If both values
** are NULL, return 0.
*/
if( combined_flags&MEM_Null ){
Index: src/vdbemem.c
==================================================================
--- src/vdbemem.c
+++ src/vdbemem.c
@@ -40,12 +40,11 @@
/* Cannot be both MEM_Int and MEM_Real at the same time */
assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
if( p->flags & MEM_Null ){
/* Cannot be both MEM_Null and some other type */
- assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob
- |MEM_RowSet|MEM_Frame|MEM_Agg))==0 );
+ assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|MEM_Agg))==0 );
/* If MEM_Null is set, then either the value is a pure NULL (the usual
** case) or it is a pointer set using sqlite3_bind_pointer() or
** sqlite3_result_pointer(). If a pointer, then MEM_Term must also be
** set.
@@ -154,11 +153,11 @@
*/
int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
#ifndef SQLITE_OMIT_UTF16
int rc;
#endif
- assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
return SQLITE_OK;
}
@@ -187,11 +186,11 @@
** blob if bPreserve is true. If bPreserve is false, any prior content
** in pMem->z is discarded.
*/
SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
assert( sqlite3VdbeCheckMemInvariants(pMem) );
- assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
testcase( pMem->db==0 );
/* If the bPreserve flag is set to true, then the memory cell must already
** contain a valid string or blob value. */
assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
@@ -275,11 +274,11 @@
**
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
int sqlite3VdbeMemMakeWriteable(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
int rc = vdbeMemAddTerminator(pMem);
if( rc ) return rc;
@@ -300,11 +299,11 @@
#ifndef SQLITE_OMIT_INCRBLOB
int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
assert( pMem->flags & MEM_Zero );
assert( pMem->flags&MEM_Blob );
- assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
/* Set nByte to the number of bytes required to store the expanded blob. */
nByte = pMem->n + pMem->u.nZero;
if( nByte<=0 ){
@@ -355,11 +354,11 @@
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !(fg&MEM_Zero) );
assert( !(fg&(MEM_Str|MEM_Blob)) );
assert( fg&(MEM_Int|MEM_Real) );
- assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
pMem->enc = 0;
@@ -460,19 +459,12 @@
sqlite3VdbeMemFinalize(p, p->u.pDef);
assert( (p->flags & MEM_Agg)==0 );
testcase( p->flags & MEM_Dyn );
}
if( p->flags&MEM_Dyn ){
- assert( (p->flags&MEM_RowSet)==0 );
assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
p->xDel((void *)p->z);
- }else if( p->flags&MEM_RowSet ){
- sqlite3RowSetClear(p->u.pRowSet);
- }else if( p->flags&MEM_Frame ){
- VdbeFrame *pFrame = p->u.pFrame;
- pFrame->pParent = pFrame->v->pDelFrame;
- pFrame->v->pDelFrame = pFrame;
}
p->flags = MEM_Null;
}
/*
@@ -616,11 +608,11 @@
** MEM_Int if we can.
*/
void sqlite3VdbeIntegerAffinity(Mem *pMem){
i64 ix;
assert( pMem->flags & MEM_Real );
- assert( (pMem->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
ix = doubleToInt64(pMem->u.r);
@@ -643,11 +635,11 @@
/*
** Convert pMem to type integer. Invalidate any prior representations.
*/
int sqlite3VdbeMemIntegerify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- assert( (pMem->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
pMem->u.i = sqlite3VdbeIntValue(pMem);
MemSetTypeFlag(pMem, MEM_Int);
return SQLITE_OK;
@@ -861,30 +853,40 @@
pMem->flags = MEM_Real;
}
}
#endif
+#ifdef SQLITE_DEBUG
+/*
+** Return true if the Mem holds a RowSet object. This routine is intended
+** for use inside of assert() statements.
+*/
+int sqlite3VdbeMemIsRowSet(const Mem *pMem){
+ return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn)
+ && pMem->xDel==sqlite3RowSetDelete;
+}
+#endif
+
/*
** Delete any previous value and set the value of pMem to be an
** empty boolean index.
+**
+** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation
+** error occurs.
*/
-void sqlite3VdbeMemSetRowSet(Mem *pMem){
+int sqlite3VdbeMemSetRowSet(Mem *pMem){
sqlite3 *db = pMem->db;
+ RowSet *p;
assert( db!=0 );
- assert( (pMem->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
- if( db->mallocFailed ){
- pMem->flags = MEM_Null;
- pMem->szMalloc = 0;
- }else{
- assert( pMem->zMalloc );
- pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
- pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
- assert( pMem->u.pRowSet!=0 );
- pMem->flags = MEM_RowSet;
- }
+ p = sqlite3RowSetInit(db);
+ if( p==0 ) return SQLITE_NOMEM;
+ pMem->z = (char*)p;
+ pMem->flags = MEM_Blob|MEM_Dyn;
+ pMem->xDel = sqlite3RowSetDelete;
+ return SQLITE_OK;
}
/*
** Return true if the Mem object contains a TEXT or BLOB that is
** too large - whose size exceeds SQLITE_MAX_LENGTH.
@@ -948,11 +950,11 @@
vdbeMemClearExternAndSetNull(pTo);
assert( !VdbeMemDynamic(pTo) );
sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
}
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
- assert( (pFrom->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pFrom) );
assert( pTo->db==pFrom->db );
if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
memcpy(pTo, pFrom, MEMCELLSIZE);
if( (pFrom->flags&MEM_Static)==0 ){
pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem);
@@ -966,11 +968,11 @@
** freed before the copy is made.
*/
int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
- assert( (pFrom->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pFrom) );
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
if( pTo->flags&(MEM_Str|MEM_Blob) ){
if( 0==(pFrom->flags&MEM_Static) ){
@@ -1024,11 +1026,11 @@
int nByte = n; /* New value for pMem->n */
int iLimit; /* Maximum allowed string or blob size */
u16 flags = 0; /* New value for pMem->flags */
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- assert( (pMem->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
sqlite3VdbeMemSetNull(pMem);
return SQLITE_OK;
@@ -1146,11 +1148,11 @@
assert( sqlite3BtreeCursorIsValid(pCur) );
assert( !VdbeMemDynamic(pMem) );
/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
** that both the BtShared and database handle mutexes are held. */
- assert( (pMem->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pMem) );
zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
assert( zData!=0 );
if( offset+amt<=available ){
pMem->z = &zData[offset];
@@ -1170,11 +1172,11 @@
*/
static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
assert( pVal!=0 );
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
- assert( (pVal->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pVal) );
assert( (pVal->flags & (MEM_Null))==0 );
if( pVal->flags & (MEM_Blob|MEM_Str) ){
if( ExpandBlob(pVal) ) return 0;
pVal->flags |= MEM_Str;
if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){
@@ -1213,11 +1215,11 @@
*/
const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
if( !pVal ) return 0;
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
- assert( (pVal->flags & MEM_RowSet)==0 );
+ assert( !sqlite3VdbeMemIsRowSet(pVal) );
if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
assert( sqlite3VdbeMemConsistentDualRep(pVal) );
return pVal->z;
}
if( pVal->flags&MEM_Null ){
Index: src/vdbesort.c
==================================================================
--- src/vdbesort.c
+++ src/vdbesort.c
@@ -2105,19 +2105,24 @@
MergeEngine *pMerger, /* MergeEngine to initialize */
int eMode /* One of the INCRINIT_XXX constants */
){
int rc = SQLITE_OK; /* Return code */
int i; /* For looping over PmaReader objects */
- int nTree = pMerger->nTree;
+ int nTree; /* Number of subtrees to merge */
+
+ /* Failure to allocate the merge would have been detected prior to
+ ** invoking this routine */
+ assert( pMerger!=0 );
/* eMode is always INCRINIT_NORMAL in single-threaded mode */
assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL );
/* Verify that the MergeEngine is assigned to a single thread */
assert( pMerger->pTask==0 );
pMerger->pTask = pTask;
+ nTree = pMerger->nTree;
for(i=0; i0 && eMode==INCRINIT_ROOT ){
/* PmaReaders should be normally initialized in order, as if they are
** reading from the same temp file this makes for more linear file IO.
** However, in the INCRINIT_ROOT case, if PmaReader aReadr[nTask-1] is
Index: src/wal.c
==================================================================
--- src/wal.c
+++ src/wal.c
@@ -256,10 +256,22 @@
# define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
#else
# define WALTRACE(X)
#endif
+/*
+** WAL mode depends on atomic aligned 32-bit loads and stores in a few
+** places. The following macros try to make this explicit.
+*/
+#if GCC_VESRION>=5004000
+# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
+# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
+#else
+# define AtomicLoad(PTR) (*(PTR))
+# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
+#endif
+
/*
** The maximum (and only) versions of the wal and wal-index formats
** that may be interpreted by this version of SQLite.
**
** If a client begins recovering a WAL file and finds that (a) the checksum
@@ -2553,11 +2565,11 @@
if( pWal->pSnapshot && pWal->pSnapshot->mxFramepSnapshot->mxFrame;
}
#endif
for(i=1; iaReadMark[i];
+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
mxI = i;
}
@@ -2566,11 +2578,11 @@
&& (mxReadMarkaReadMark[i] = mxFrame;
+ mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
mxI = i;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
break;
}else if( rc!=SQLITE_BUSY ){
return rc;
@@ -2618,13 +2630,13 @@
** frame pWal->hdr.mxFrame - then the client would incorrectly assume
** that it can read version A from the database file. However, since
** we can guarantee that the checkpointer that set nBackfill could not
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
*/
- pWal->minFrame = pInfo->nBackfill+1;
+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
walShmBarrier(pWal);
- if( pInfo->aReadMark[mxI]!=mxReadMark
+ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
){
walUnlockShared(pWal, WAL_READ_LOCK(mxI));
return WAL_RETRY;
}else{
@@ -2781,25 +2793,26 @@
if( rc==SQLITE_OK ){
/* Check that the wal file has not been wrapped. Assuming that it has
** not, also check that no checkpointer has attempted to checkpoint any
** frames beyond pSnapshot->mxFrame. If either of these conditions are
- ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
+ ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
** with *pSnapshot and set *pChanged as appropriate for opening the
** snapshot. */
if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
&& pSnapshot->mxFrame>=pInfo->nBackfillAttempted
){
assert( pWal->readLock>0 );
memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
*pChanged = bChanged;
}else{
- rc = SQLITE_BUSY_SNAPSHOT;
+ rc = SQLITE_ERROR_SNAPSHOT;
}
/* Release the shared CKPT lock obtained above. */
walUnlockShared(pWal, WAL_CKPT_LOCK);
+ pWal->minFrame = 1;
}
if( rc!=SQLITE_OK ){
sqlite3WalEndReadTransaction(pWal);
@@ -3787,11 +3800,11 @@
if( rc==SQLITE_OK ){
WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
|| pNew->mxFramenBackfillAttempted
){
- rc = SQLITE_BUSY_SNAPSHOT;
+ rc = SQLITE_ERROR_SNAPSHOT;
walUnlockShared(pWal, WAL_CKPT_LOCK);
}
}
return rc;
}
Index: src/whereexpr.c
==================================================================
--- src/whereexpr.c
+++ src/whereexpr.c
@@ -338,10 +338,11 @@
** of virtual table in forms (5) or (7) then return 2.
**
** If the expression matches none of the patterns above, return 0.
*/
static int isAuxiliaryVtabOperator(
+ sqlite3 *db, /* Parsing context */
Expr *pExpr, /* Test this expression */
unsigned char *peOp2, /* OUT: 0 for MATCH, or else an op2 value */
Expr **ppLeft, /* Column expression to left of MATCH/op2 */
Expr **ppRight /* Expression to left of MATCH/op2 */
){
@@ -361,20 +362,58 @@
pList = pExpr->x.pList;
if( pList==0 || pList->nExpr!=2 ){
return 0;
}
+
+ /* Built-in operators MATCH, GLOB, LIKE, and REGEXP attach to a
+ ** virtual table on their second argument, which is the same as
+ ** the left-hand side operand in their in-fix form.
+ **
+ ** vtab_column MATCH expression
+ ** MATCH(expression,vtab_column)
+ */
pCol = pList->a[1].pExpr;
- if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){
- return 0;
- }
- for(i=0; iu.zToken, aOp[i].zOp)==0 ){
- *peOp2 = aOp[i].eOp2;
- *ppRight = pList->a[0].pExpr;
- *ppLeft = pCol;
- return 1;
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
+ for(i=0; iu.zToken, aOp[i].zOp)==0 ){
+ *peOp2 = aOp[i].eOp2;
+ *ppRight = pList->a[0].pExpr;
+ *ppLeft = pCol;
+ return 1;
+ }
+ }
+ }
+
+ /* We can also match against the first column of overloaded
+ ** functions where xFindFunction returns a value of at least
+ ** SQLITE_INDEX_CONSTRAINT_FUNCTION.
+ **
+ ** OVERLOADED(vtab_column,expression)
+ **
+ ** Historically, xFindFunction expected to see lower-case function
+ ** names. But for this use case, xFindFunction is expected to deal
+ ** with function names in an arbitrary case.
+ */
+ pCol = pList->a[0].pExpr;
+ if( pCol->op==TK_COLUMN && IsVirtual(pCol->pTab) ){
+ sqlite3_vtab *pVtab;
+ sqlite3_module *pMod;
+ void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**);
+ void *pNotUsed;
+ pVtab = sqlite3GetVTable(db, pCol->pTab)->pVtab;
+ assert( pVtab!=0 );
+ assert( pVtab->pModule!=0 );
+ pMod = (sqlite3_module *)pVtab->pModule;
+ if( pMod->xFindFunction!=0 ){
+ i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed);
+ if( i>=SQLITE_INDEX_CONSTRAINT_FUNCTION ){
+ *peOp2 = i;
+ *ppRight = pList->a[1].pExpr;
+ *ppLeft = pCol;
+ return 1;
+ }
}
}
}else if( pExpr->op==TK_NE || pExpr->op==TK_ISNOT || pExpr->op==TK_NOTNULL ){
int res = 0;
Expr *pLeft = pExpr->pLeft;
@@ -818,11 +857,11 @@
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
pNew->x.pList = pList;
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
- pTerm = &pWC->a[idxTerm];
+ /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */
markTermAsChild(pWC, idxNew, idxTerm);
}else{
sqlite3ExprListDelete(db, pList);
}
}
@@ -1235,11 +1274,11 @@
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
if( pWC->op==TK_AND ){
Expr *pRight = 0, *pLeft = 0;
- int res = isAuxiliaryVtabOperator(pExpr, &eOp2, &pLeft, &pRight);
+ int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
while( res-- > 0 ){
int idxNew;
WhereTerm *pNewTerm;
Bitmask prereqColumn, prereqExpr;
Index: test/alter.test
==================================================================
--- test/alter.test
+++ test/alter.test
@@ -679,25 +679,23 @@
SELECT a, sum(b) FROM t2 GROUP BY a;
}
} {1 18 2 9}
#--------------------------------------------------------------------------
-# alter-9.X - Special test: Make sure the sqlite_rename_trigger() and
+# alter-9.X - Special test: Make sure the sqlite_rename_column() and
# rename_table() functions do not crash when handed bad input.
#
-ifcapable trigger {
- do_test alter-9.1 {
- execsql {SELECT SQLITE_RENAME_TRIGGER(0,0)}
- } {{}}
-}
-do_test alter-9.2 {
- execsql {
- SELECT SQLITE_RENAME_TABLE(0,0);
- SELECT SQLITE_RENAME_TABLE(10,20);
- SELECT SQLITE_RENAME_TABLE('foo', 'foo');
- }
-} {{} {} {}}
+do_test alter-9.1 {
+ execsql {SELECT SQLITE_RENAME_COLUMN(0,0,0,0,0,0,0,0)}
+} {{}}
+foreach {tn sql} {
+ 1 { SELECT SQLITE_RENAME_TABLE(0,0,0,0,0) }
+ 2 { SELECT SQLITE_RENAME_TABLE(10,20,30,40,50) }
+ 3 { SELECT SQLITE_RENAME_TABLE('foo', 'foo', 'foo', 'foo', 'foo') }
+} {
+ do_catchsql_test alter-9.2.$tn $sql {1 {SQL logic error}}
+}
#------------------------------------------------------------------------
# alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters
# in the names.
#
@@ -873,53 +871,7 @@
do_execsql_test alter-16.2 {
ALTER TABLE t16a RENAME TO t16a_rn;
SELECT * FROM t16a_rn ORDER BY a;
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
-#-------------------------------------------------------------------------
-# Verify that NULL values into the internal-use-only sqlite_rename_*()
-# functions do not cause problems.
-#
-do_execsql_test alter-17.1 {
- SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)','abc');
-} {{CREATE TABLE "abc"(a,b,c)}}
-do_execsql_test alter-17.2 {
- SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)',NULL);
-} {{CREATE TABLE "(NULL)"(a,b,c)}}
-do_execsql_test alter-17.3 {
- SELECT sqlite_rename_table(NULL,'abc');
-} {{}}
-do_execsql_test alter-17.4 {
- SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN','abc');
-} {{CREATE TRIGGER r1 ON "abc" WHEN}}
-do_execsql_test alter-17.5 {
- SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN',NULL);
-} {{CREATE TRIGGER r1 ON "(NULL)" WHEN}}
-do_execsql_test alter-17.6 {
- SELECT sqlite_rename_trigger(NULL,'abc');
-} {{}}
-do_execsql_test alter-17.7 {
- SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
- 'xyzzy','lmnop');
-} {{CREATE TABLE t1(a REFERENCES "lmnop")}}
-do_execsql_test alter-17.8 {
- SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
- 'xyzzy',NULL);
-} {{CREATE TABLE t1(a REFERENCES "(NULL)")}}
-do_execsql_test alter-17.9 {
- SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
- NULL, 'lmnop');
-} {{}}
-do_execsql_test alter-17.10 {
- SELECT sqlite_rename_parent(NULL,'abc','xyz');
-} {{}}
-do_execsql_test alter-17.11 {
- SELECT sqlite_rename_parent('create references ''','abc','xyz');
-} {{create references '}}
-do_execsql_test alter-17.12 {
- SELECT sqlite_rename_parent('create references "abc"123" ','abc','xyz');
-} {{create references "xyz"123" }}
-do_execsql_test alter-17.13 {
- SELECT sqlite_rename_parent("references '''",'abc','xyz');
-} {{references '''}}
-
finish_test
+
Index: test/altercol.test
==================================================================
--- test/altercol.test
+++ test/altercol.test
@@ -652,48 +652,61 @@
SELECT sql FROM sqlite_master WHERE type='view';
} {{CREATE VIEW vvv AS SELECT xyz AS d FROM xxx WHERE d=0}}
#-------------------------------------------------------------------------
#
-do_execsql_test 16.0 {
+do_execsql_test 16.1.0 {
CREATE TABLE t1(a,b,c);
CREATE TABLE t2(d,e,f);
INSERT INTO t1 VALUES(1,2,3);
INSERT INTO t2 VALUES(4,5,6);
CREATE VIEW v4 AS SELECT a, d FROM t1, t2;
SELECT * FROM v4;
} {1 4}
-do_catchsql_test 16.1 {
+do_catchsql_test 16.1.1 {
ALTER TABLE t2 RENAME d TO a;
} {1 {error in view v4 after rename: ambiguous column name: a}}
-do_execsql_test 16.2 {
+do_execsql_test 16.1.2 {
SELECT * FROM v4;
} {1 4}
-do_execsql_test 16.3 {
+do_execsql_test 16.1.3 {
CREATE UNIQUE INDEX t2d ON t2(d);
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
INSERT INTO t2 VALUES(new.a, new.b, new.c)
ON CONFLICT(d) DO UPDATE SET f = excluded.f;
END;
}
-do_execsql_test 16.4 {
+do_execsql_test 16.1.4 {
INSERT INTO t1 VALUES(4, 8, 456);
SELECT * FROM t2;
} {4 5 456}
-do_execsql_test 16.5 {
+do_execsql_test 16.1.5 {
ALTER TABLE t2 RENAME COLUMN f TO "big f";
INSERT INTO t1 VALUES(4, 0, 20456);
SELECT * FROM t2;
} {4 5 20456}
-do_execsql_test 16.6 {
+do_execsql_test 16.1.6 {
ALTER TABLE t1 RENAME COLUMN c TO "big c";
INSERT INTO t1 VALUES(4, 0, 0);
SELECT * FROM t2;
} {4 5 0}
+
+do_execsql_test 16.2.1 {
+ CREATE VIEW temp.v5 AS SELECT "big c" FROM t1;
+ SELECT * FROM v5;
+} {3 456 20456 0}
+
+do_execsql_test 16.2.2 {
+ ALTER TABLE t1 RENAME COLUMN "big c" TO reallybigc;
+} {}
+
+do_execsql_test 16.2.3 {
+ SELECT * FROM v5;
+} {3 456 20456 0}
finish_test
ADDED test/altertab.test
Index: test/altertab.test
==================================================================
--- /dev/null
+++ test/altertab.test
@@ -0,0 +1,210 @@
+# 2018 August 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.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix altertab
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b, CHECK(t1.a != t1.b));
+
+ CREATE TABLE t2(a, b);
+ CREATE INDEX t2expr ON t2(a) WHERE t2.b>0;
+}
+
+do_execsql_test 1.1 {
+ SELECT sql FROM sqlite_master
+} {
+ {CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))}
+ {CREATE TABLE t2(a, b)}
+ {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
+}
+
+do_execsql_test 1.2 {
+ ALTER TABLE t1 RENAME TO t1new;
+}
+
+do_execsql_test 1.3 {
+ CREATE TABLE t3(c, d);
+ ALTER TABLE t3 RENAME TO t3new;
+ DROP TABLE t3new;
+}
+
+do_execsql_test 1.4 {
+ SELECT sql FROM sqlite_master
+} {
+ {CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
+ {CREATE TABLE t2(a, b)}
+ {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0}
+}
+
+
+do_execsql_test 1.3 {
+ ALTER TABLE t2 RENAME TO t2new;
+}
+do_execsql_test 1.4 {
+ SELECT sql FROM sqlite_master
+} {
+ {CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))}
+ {CREATE TABLE "t2new"(a, b)}
+ {CREATE INDEX t2expr ON "t2new"(a) WHERE "t2new".b>0}
+}
+
+
+#-------------------------------------------------------------------------
+reset_db
+register_echo_module db
+
+do_execsql_test 2.0 {
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES(1, 2, 3);
+ CREATE VIRTUAL TABLE eee USING echo('abc');
+ SELECT * FROM eee;
+} {1 2 3}
+
+do_execsql_test 2.1 {
+ ALTER TABLE eee RENAME TO fff;
+ SELECT * FROM fff;
+} {1 2 3}
+
+db close
+sqlite3 db test.db
+
+do_catchsql_test 2.2 {
+ ALTER TABLE fff RENAME TO ggg;
+} {1 {no such module: echo}}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 3.0 {
+ CREATE TABLE txx(a, b, c);
+ INSERT INTO txx VALUES(1, 2, 3);
+ CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx;
+ CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one;
+ CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx;
+}
+
+do_execsql_test 3.1.1 {
+ SELECT * FROM vvv;
+} {1 2 3}
+do_execsql_test 3.1.2 {
+ ALTER TABLE txx RENAME TO "t xx";
+ SELECT * FROM vvv;
+} {1 2 3}
+do_execsql_test 3.1.3 {
+ SELECT sql FROM sqlite_master WHERE name='vvv';
+} {{CREATE VIEW vvv AS SELECT main."t xx".a, "t xx".b, c FROM "t xx"}}
+
+
+do_execsql_test 3.2.1 {
+ SELECT * FROM uuu;
+} {1 2 3}
+do_execsql_test 3.2.2 {
+ SELECT sql FROM sqlite_master WHERE name='uuu';;
+} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM "t xx" AS one}}
+
+do_execsql_test 3.3.1 {
+ SELECT * FROM ttt;
+} {1 2 2 1}
+do_execsql_test 3.3.2 {
+ SELECT sql FROM sqlite_temp_master WHERE name='ttt';
+} {{CREATE VIEW ttt AS SELECT main."t xx".a, "t xx".b, one.b, main.one.a FROM "t xx" AS one, "t xx"}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 4.0 {
+ CREATE table t1(x, y);
+ CREATE table t2(a, b);
+
+ CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
+ SELECT t1.x, * FROM t1, t2;
+ INSERT INTO t2 VALUES(new.x, new.y);
+ END;
+}
+
+do_execsql_test 4.1 {
+ INSERT INTO t1 VALUES(1, 1);
+ ALTER TABLE t1 RENAME TO t11;
+ INSERT INTO t11 VALUES(2, 2);
+ ALTER TABLE t2 RENAME TO t22;
+ INSERT INTO t11 VALUES(3, 3);
+}
+
+proc squish {a} {
+ string trim [regsub -all {[[:space:]][[:space:]]*} $a { }]
+}
+db func squish squish
+do_test 4.2 {
+ execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' }
+} [list [squish {
+ CREATE TRIGGER tr1 AFTER INSERT ON "t11" BEGIN
+ SELECT "t11".x, * FROM "t11", "t22";
+ INSERT INTO "t22" VALUES(new.x, new.y);
+ END
+}]]
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t9(a, b, c);
+ CREATE TABLE t10(a, b, c);
+ CREATE TEMP TABLE t9(a, b, c);
+
+ CREATE TRIGGER temp.t9t AFTER INSERT ON temp.t9 BEGIN
+ INSERT INTO t10 VALUES(new.a, new.b, new.c);
+ END;
+
+ INSERT INTO temp.t9 VALUES(1, 2, 3);
+ SELECT * FROM t10;
+} {1 2 3}
+
+do_execsql_test 5.1 {
+ ALTER TABLE temp.t9 RENAME TO 't1234567890'
+}
+
+do_execsql_test 5.2 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t2 VALUES(3, 4);
+ CREATE VIEW v AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
+ SELECT * FROM v;
+} {1 2 3 4}
+
+do_catchsql_test 5.3 {
+ ALTER TABLE t2 RENAME TO one;
+} {1 {error in view v after rename: ambiguous column name: one.a}}
+
+do_execsql_test 5.4 {
+ SELECT * FROM v
+} {1 2 3 4}
+
+do_execsql_test 5.5 {
+ DROP VIEW v;
+ CREATE VIEW temp.vv AS SELECT one.a, one.b, t2.a, t2.b FROM t1 AS one, t2;
+ SELECT * FROM vv;
+} {1 2 3 4}
+
+do_catchsql_test 5.6 {
+ ALTER TABLE t2 RENAME TO one;
+} {1 {error in view vv after rename: ambiguous column name: one.a}}
+
+finish_test
+
+
Index: test/fkey2.test
==================================================================
--- test/fkey2.test
+++ test/fkey2.test
@@ -981,11 +981,11 @@
# Test the sqlite_rename_parent() function directly.
#
proc test_rename_parent {zCreate zOld zNew} {
- db eval {SELECT sqlite_rename_parent($zCreate, $zOld, $zNew)}
+ db eval {SELECT sqlite_rename_table('main', $zCreate, $zOld, $zNew, 0)}
}
do_test fkey2-14.2.1.1 {
test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3
} {{CREATE TABLE t1(a REFERENCES "t3")}}
do_test fkey2-14.2.1.2 {
Index: test/server1.test
==================================================================
--- test/server1.test
+++ test/server1.test
@@ -23,10 +23,19 @@
#
if {[llength [info command client_step]]==0 || [sqlite3 -has-codec]} {
finish_test
return
}
+
+# This test does not work on older PPC Macs due to problems in the
+# pthreads library. So skip it.
+#
+if {$tcl_platform(machine)=="Power Macintosh" &&
+ $tcl_platform(byteOrder)=="bigEndian"} {
+ finish_test
+ return
+}
# The sample server implementation does not work right when memory
# management is enabled.
#
ifcapable (memorymanage||mutex_noop) {
Index: test/snapshot.test
==================================================================
--- test/snapshot.test
+++ test/snapshot.test
@@ -256,11 +256,11 @@
snapshot_free $snapshot
execsql COMMIT
} {}
#-------------------------------------------------------------------------
- # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
+ # Check that SQLITE_ERROR_SNAPSHOT is returned if the specified snapshot
# no longer exists because the wal file has been checkpointed.
#
# 1. Reading a snapshot from the middle of a wal file is not possible
# after the wal file has been checkpointed.
#
@@ -294,11 +294,11 @@
COMMIT;
PRAGMA wal_checkpoint;
BEGIN;
}
list [catch {snapshot_open db main $snapshot} msg] $msg
- } {1 SQLITE_BUSY_SNAPSHOT}
+ } {1 SQLITE_ERROR_SNAPSHOT}
do_test $tn.4.1.4 {
snapshot_free $snapshot
execsql COMMIT
} {}
@@ -325,11 +325,11 @@
COMMIT;
INSERT INTO t3 VALUES('e', 't');
BEGIN;
}
list [catch {snapshot_open db main $snapshot} msg] $msg
- } {1 SQLITE_BUSY_SNAPSHOT}
+ } {1 SQLITE_ERROR_SNAPSHOT}
do_test $tn.4.2.4 {
snapshot_free $snapshot
} {}
#-------------------------------------------------------------------------
Index: test/snapshot2.test
==================================================================
--- test/snapshot2.test
+++ test/snapshot2.test
@@ -108,11 +108,11 @@
sqlite3 db test.db
execsql {SELECT * FROM sqlite_master}
execsql BEGIN
list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
do_test 2.3 {
execsql COMMIT
sqlite3_snapshot_recover db main
execsql BEGIN
@@ -132,11 +132,11 @@
sqlite3 db test.db
sqlite3_snapshot_recover db main
execsql BEGIN
list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
#-------------------------------------------------------------------------
# Check that calling sqlite3_snapshot_recover() does not confuse the
# pager cache.
reset_db
@@ -232,10 +232,10 @@
do_test 5.4 {
execsql { INSERT INTO t2 VALUES('jkl') }
execsql BEGIN db2
list [catch { sqlite3_snapshot_open_blob db2 main $snap } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
finish_test
Index: test/snapshot3.test
==================================================================
--- test/snapshot3.test
+++ test/snapshot3.test
@@ -92,9 +92,9 @@
} 0
do_test 1.8 {
execsql BEGIN db3
list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
finish_test
ADDED test/snapshot4.test
Index: test/snapshot4.test
==================================================================
--- /dev/null
+++ test/snapshot4.test
@@ -0,0 +1,75 @@
+# 2018 August 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 implements regression tests for SQLite library. The focus
+# of this file is the sqlite3_snapshot_xxx() APIs.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !snapshot {finish_test; return}
+set testprefix snapshot4
+
+# This test does not work with the inmemory_journal permutation. The reason
+# is that each connection opened as part of this permutation executes
+# "PRAGMA journal_mode=memory", which fails if the database is in wal mode
+# and there are one or more existing connections.
+if {[permutation]=="inmemory_journal"} {
+ finish_test
+ return
+}
+
+sqlite3 db2 test.db
+
+do_execsql_test 1.0 {
+ PRAGMA cache_size = 10;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, randomblob(400));
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 2 UNION ALL SELECT i+1 FROM s WHERE i<100
+ )
+ INSERT INTO t1 SELECT i, randomblob(400) FROM s;
+} {wal}
+
+do_test 1.1 {
+ execsql {
+ BEGIN;
+ SELECT count(*) FROM t1;
+ }
+} {100}
+
+do_test 1.2 {
+ db2 eval {
+ SELECT count(*) FROM t1;
+ CREATE TABLE t2(x);
+ }
+} {100}
+
+do_test 1.3 {
+ set ::snap [sqlite3_snapshot_get_blob db main]
+ db2 eval { PRAGMA wal_checkpoint }
+} {0 54 52}
+
+do_test 1.4 {
+ execsql {
+ COMMIT;
+ SELECT * FROM sqlite_master;
+ BEGIN;
+ }
+ sqlite3_snapshot_open_blob db main $::snap
+ execsql {
+ SELECT count(*) FROM t1
+ }
+} {100}
+
+
+finish_test
+
Index: test/snapshot_fault.test
==================================================================
--- test/snapshot_fault.test
+++ test/snapshot_fault.test
@@ -45,11 +45,11 @@
} -body {
db eval { PRAGMA wal_checkpoint }
} -test {
db2 eval BEGIN
if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
- if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
+ if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
error "error is $msg"
}
} else {
set res [db2 eval {
SELECT a FROM t1;
@@ -96,11 +96,11 @@
db_restore_and_reopen
db eval { SELECT * FROM t1 }
db eval BEGIN
if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
- if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
+ if {$msg != "SQLITE_ERROR_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
error "error is $msg"
}
} else {
# This branch should actually never be taken. But it was useful in
# determining whether or not this test was actually working (by
Index: test/snapshot_up.test
==================================================================
--- test/snapshot_up.test
+++ test/snapshot_up.test
@@ -73,20 +73,20 @@
SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}
do_test 1.7 {
list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
do_execsql_test 1.8 {
SELECT * FROM t1
} {1 2 3 4 5 6 7 8 9 10 11 12}
do_test 1.9 {
execsql { COMMIT ; BEGIN }
list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
do_test 1.10 {
execsql { COMMIT }
execsql {
PRAGMA wal_checkpoint;
@@ -111,11 +111,11 @@
execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}
do_test 1.13 {
list [catch { sqlite3_snapshot_open db main $::snap3 } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
do_test 1.14 {
execsql { SELECT * FROM t1 }
} {4 5 6 7 8 9 10 11 12 13 14 15}
db close
@@ -125,11 +125,11 @@
BEGIN;
SELECT * FROM t1
} {7 8 9 10 11 12 13 14 15}
do_test 1.16 {
list [catch { sqlite3_snapshot_open db main $::snap4 } msg] $msg
-} {1 SQLITE_BUSY_SNAPSHOT}
+} {1 SQLITE_ERROR_SNAPSHOT}
do_execsql_test 1.17 { COMMIT }
sqlite3_snapshot_free $::snap1
sqlite3_snapshot_free $::snap2
sqlite3_snapshot_free $::snap3
Index: test/view.test
==================================================================
--- test/view.test
+++ test/view.test
@@ -689,12 +689,15 @@
set log ""
db authorizer ::authLogDelete
db eval {DROP VIEW x1;}
set log
} {}
+
+set res [list {SQLITE_DELETE sqlite_stat1 {} main {} {}}]
+ifcapable stat4 { lappend res {SQLITE_DELETE sqlite_stat4 {} main {} {}} }
do_test view-25.2 {
set log ""
db eval {DROP TABLE t25;}
set log
-} {{SQLITE_DELETE sqlite_stat1 {} main {} {}}}
+} $res
finish_test
Index: test/without_rowid3.test
==================================================================
--- test/without_rowid3.test
+++ test/without_rowid3.test
@@ -947,11 +947,11 @@
# Test the sqlite_rename_parent() function directly.
#
proc test_rename_parent {zCreate zOld zNew} {
- db eval {SELECT sqlite_rename_parent($zCreate, $zOld, $zNew)}
+ db eval {SELECT sqlite_rename_table('main', $zCreate, $zOld, $zNew, 0)}
}
do_test without_rowid3-14.2.1.1 {
test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3
} {{CREATE TABLE t1(a REFERENCES "t3")}}
do_test without_rowid3-14.2.1.2 {
Index: tool/mksqlite3c.tcl
==================================================================
--- tool/mksqlite3c.tcl
+++ tool/mksqlite3c.tcl
@@ -97,10 +97,11 @@
btreeInt.h
fts3.h
fts3Int.h
fts3_hash.h
fts3_tokenizer.h
+ geopoly.c
hash.h
hwtime.h
keywordhash.h
msvc.h
mutex.h
@@ -390,18 +391,18 @@
fts3_write.c
fts3_snippet.c
fts3_unicode.c
fts3_unicode2.c
+ json1.c
rtree.c
icu.c
fts3_icu.c
sqlite3rbu.c
dbstat.c
dbpage.c
sqlite3session.c
- json1.c
fts5.c
stmt.c
} {
copy_file tsrc/$file
}