Bläddra i källkod

Add unql as extension, but unql itself is bug, it can works for small sets because it allays do linear scan

mingodad 10 år sedan
förälder
incheckning
882772340a
25 ändrade filer med 11825 tillägg och 0 borttagningar
  1. 116 0
      unql/libunql.cbp
  2. 182 0
      unql/libunql.mak
  3. 89 0
      unql/src/complete.c
  4. 148 0
      unql/src/conn.c
  5. 48 0
      unql/src/context.c
  6. 634 0
      unql/src/datasrc.c
  7. 75 0
      unql/src/delete.c
  8. 843 0
      unql/src/expr.c
  9. 489 0
      unql/src/func.c
  10. 868 0
      unql/src/json.c
  11. 850 0
      unql/src/lempar.c
  12. 149 0
      unql/src/memory.c
  13. 2196 0
      unql/src/parse.c
  14. 84 0
      unql/src/parse.h
  15. 703 0
      unql/src/parse.y
  16. 32 0
      unql/src/pragma.c
  17. 774 0
      unql/src/query.c
  18. 704 0
      unql/src/shell.c
  19. 322 0
      unql/src/stmt.c
  20. 762 0
      unql/src/string.c
  21. 496 0
      unql/src/tokenize.c
  22. 396 0
      unql/src/trace.c
  23. 240 0
      unql/src/update.c
  24. 97 0
      unql/src/xjd1.h
  25. 528 0
      unql/src/xjd1Int.h

+ 116 - 0
unql/libunql.cbp

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="libunql" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Debug">
+				<Option output="unql" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="" />
+				<Option object_output="obj/Debug/" />
+				<Option type="2" />
+				<Option compiler="gcc" />
+				<Option createDefFile="1" />
+				<Compiler>
+					<Add option="-Wall" />
+					<Add option="-g" />
+				</Compiler>
+			</Target>
+			<Target title="Release">
+				<Option output="unql" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="" />
+				<Option object_output="obj/Release/" />
+				<Option type="2" />
+				<Option compiler="gcc" />
+				<Option createDefFile="1" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-Wall" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-DTHREADSAFE=1" />
+			<Add option="-DSQLITE_DEFAULT_FILE_FORMAT=4" />
+			<Add option="-DSQLITE_DEFAULT_AUTOVACUUM=1" />
+			<Add option="-DSQLITE_DEFAULT_FOREIGN_KEYS=1" />
+			<Add option="-DSQLITE_ENABLE_COLUMN_METADATA=1" />
+			<Add option="-DSQLITE_ENABLE_EXTENSION_FUNCTIONS=1" />
+			<Add option="-DSQLITE_ENABLE_FTS4=1" />
+			<Add option="-DSQLITE_ENABLE_FTS3_PARENTHESIS=1" />
+			<Add option="-DSQLITE_ENABLE_UNLOCK_NOTIFY=1" />
+			<Add option="-DSQLITE_ENABLE_RTREE=1" />
+			<Add option="-DSQLITE_ENABLE_STAT4=1" />
+			<Add option="-DSQLITE_HAS_CODEC=1" />
+			<Add option="-DSQLITE_OMIT_TCL_VARIABLE=1" />
+			<Add option="-DSQLITE_USE_URI=1" />
+			<Add option="-DSQLITE_SOUNDEX=1" />
+			<Add directory="src" />
+			<Add directory="/home/mingo/dev/dadbiz++/third-party/unql/" />
+		</Compiler>
+		<Unit filename="src/complete.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/conn.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/context.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/datasrc.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/delete.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/expr.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/func.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/json.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/memory.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/parse.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/parse.h" />
+		<Unit filename="src/pragma.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/query.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/stmt.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/string.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/tokenize.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/trace.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/update.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="src/xjd1.h" />
+		<Unit filename="src/xjd1Int.h" />
+		<Extensions>
+			<code_completion />
+			<envvars />
+			<debugger />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>

+ 182 - 0
unql/libunql.mak

@@ -0,0 +1,182 @@
+#------------------------------------------------------------------------------#
+# This makefile was generated by 'cbp2make' tool rev.135                       #
+#------------------------------------------------------------------------------#
+
+
+WORKDIR = `pwd`
+
+CC = gcc
+CXX = g++
+AR = ar
+LD = g++
+WINDRES = windres
+
+INC =  -Isrc -I/home/mingo/dev/dadbiz++/third-party/unql/
+CFLAGS =  -DTHREADSAFE=1 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_EXTENSION_FUNCTIONS=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_UNLOCK_NOTIFY=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_STAT4=1 -DSQLITE_HAS_CODEC=1 -DSQLITE_OMIT_TCL_VARIABLE=1 -DSQLITE_USE_URI=1 -DSQLITE_SOUNDEX=1
+RESINC = 
+LIBDIR = 
+LIB = 
+LDFLAGS = 
+
+INC_DEBUG =  $(INC)
+CFLAGS_DEBUG =  $(CFLAGS) -Wall -g
+RESINC_DEBUG =  $(RESINC)
+RCFLAGS_DEBUG =  $(RCFLAGS)
+LIBDIR_DEBUG =  $(LIBDIR)
+LIB_DEBUG = $(LIB)
+LDFLAGS_DEBUG =  $(LDFLAGS)
+OBJDIR_DEBUG = obj/Debug
+DEP_DEBUG = 
+OUT_DEBUG = unql.a
+
+INC_RELEASE =  $(INC)
+CFLAGS_RELEASE =  $(CFLAGS) -O2 -Wall
+RESINC_RELEASE =  $(RESINC)
+RCFLAGS_RELEASE =  $(RCFLAGS)
+LIBDIR_RELEASE =  $(LIBDIR)
+LIB_RELEASE = $(LIB)
+LDFLAGS_RELEASE =  $(LDFLAGS) -s
+OBJDIR_RELEASE = obj/Release
+DEP_RELEASE = 
+OUT_RELEASE = unql.a
+
+OBJ_DEBUG = $(OBJDIR_DEBUG)/src/update.o $(OBJDIR_DEBUG)/src/trace.o $(OBJDIR_DEBUG)/src/tokenize.o $(OBJDIR_DEBUG)/src/string.o $(OBJDIR_DEBUG)/src/stmt.o $(OBJDIR_DEBUG)/src/query.o $(OBJDIR_DEBUG)/src/pragma.o $(OBJDIR_DEBUG)/src/complete.o $(OBJDIR_DEBUG)/src/parse.o $(OBJDIR_DEBUG)/src/memory.o $(OBJDIR_DEBUG)/src/json.o $(OBJDIR_DEBUG)/src/func.o $(OBJDIR_DEBUG)/src/expr.o $(OBJDIR_DEBUG)/src/delete.o $(OBJDIR_DEBUG)/src/datasrc.o $(OBJDIR_DEBUG)/src/context.o $(OBJDIR_DEBUG)/src/conn.o
+
+OBJ_RELEASE = $(OBJDIR_RELEASE)/src/update.o $(OBJDIR_RELEASE)/src/trace.o $(OBJDIR_RELEASE)/src/tokenize.o $(OBJDIR_RELEASE)/src/string.o $(OBJDIR_RELEASE)/src/stmt.o $(OBJDIR_RELEASE)/src/query.o $(OBJDIR_RELEASE)/src/pragma.o $(OBJDIR_RELEASE)/src/complete.o $(OBJDIR_RELEASE)/src/parse.o $(OBJDIR_RELEASE)/src/memory.o $(OBJDIR_RELEASE)/src/json.o $(OBJDIR_RELEASE)/src/func.o $(OBJDIR_RELEASE)/src/expr.o $(OBJDIR_RELEASE)/src/delete.o $(OBJDIR_RELEASE)/src/datasrc.o $(OBJDIR_RELEASE)/src/context.o $(OBJDIR_RELEASE)/src/conn.o
+
+all: debug release
+
+clean: clean_debug clean_release
+
+before_debug: 
+	test -d $(OBJDIR_DEBUG)/src || mkdir -p $(OBJDIR_DEBUG)/src
+
+after_debug: 
+
+debug: before_debug out_debug after_debug
+
+out_debug: $(OBJ_DEBUG) $(DEP_DEBUG)
+	$(AR) rcs $(OUT_DEBUG) $(OBJ_DEBUG)
+
+$(OBJDIR_DEBUG)/src/update.o: src/update.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/update.c -o $(OBJDIR_DEBUG)/src/update.o
+
+$(OBJDIR_DEBUG)/src/trace.o: src/trace.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/trace.c -o $(OBJDIR_DEBUG)/src/trace.o
+
+$(OBJDIR_DEBUG)/src/tokenize.o: src/tokenize.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/tokenize.c -o $(OBJDIR_DEBUG)/src/tokenize.o
+
+$(OBJDIR_DEBUG)/src/string.o: src/string.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/string.c -o $(OBJDIR_DEBUG)/src/string.o
+
+$(OBJDIR_DEBUG)/src/stmt.o: src/stmt.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/stmt.c -o $(OBJDIR_DEBUG)/src/stmt.o
+
+$(OBJDIR_DEBUG)/src/query.o: src/query.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/query.c -o $(OBJDIR_DEBUG)/src/query.o
+
+$(OBJDIR_DEBUG)/src/pragma.o: src/pragma.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/pragma.c -o $(OBJDIR_DEBUG)/src/pragma.o
+
+$(OBJDIR_DEBUG)/src/complete.o: src/complete.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/complete.c -o $(OBJDIR_DEBUG)/src/complete.o
+
+$(OBJDIR_DEBUG)/src/parse.o: src/parse.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/parse.c -o $(OBJDIR_DEBUG)/src/parse.o
+
+$(OBJDIR_DEBUG)/src/memory.o: src/memory.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/memory.c -o $(OBJDIR_DEBUG)/src/memory.o
+
+$(OBJDIR_DEBUG)/src/json.o: src/json.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/json.c -o $(OBJDIR_DEBUG)/src/json.o
+
+$(OBJDIR_DEBUG)/src/func.o: src/func.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/func.c -o $(OBJDIR_DEBUG)/src/func.o
+
+$(OBJDIR_DEBUG)/src/expr.o: src/expr.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/expr.c -o $(OBJDIR_DEBUG)/src/expr.o
+
+$(OBJDIR_DEBUG)/src/delete.o: src/delete.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/delete.c -o $(OBJDIR_DEBUG)/src/delete.o
+
+$(OBJDIR_DEBUG)/src/datasrc.o: src/datasrc.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/datasrc.c -o $(OBJDIR_DEBUG)/src/datasrc.o
+
+$(OBJDIR_DEBUG)/src/context.o: src/context.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/context.c -o $(OBJDIR_DEBUG)/src/context.o
+
+$(OBJDIR_DEBUG)/src/conn.o: src/conn.c
+	$(CC) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/conn.c -o $(OBJDIR_DEBUG)/src/conn.o
+
+clean_debug: 
+	rm -f $(OBJ_DEBUG) $(OUT_DEBUG)
+	rm -rf $(OBJDIR_DEBUG)/src
+
+before_release: 
+	test -d $(OBJDIR_RELEASE)/src || mkdir -p $(OBJDIR_RELEASE)/src
+
+after_release: 
+
+release: before_release out_release after_release
+
+out_release: $(OBJ_RELEASE) $(DEP_RELEASE)
+	$(AR) rcs $(OUT_RELEASE) $(OBJ_RELEASE)
+
+$(OBJDIR_RELEASE)/src/update.o: src/update.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/update.c -o $(OBJDIR_RELEASE)/src/update.o
+
+$(OBJDIR_RELEASE)/src/trace.o: src/trace.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/trace.c -o $(OBJDIR_RELEASE)/src/trace.o
+
+$(OBJDIR_RELEASE)/src/tokenize.o: src/tokenize.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/tokenize.c -o $(OBJDIR_RELEASE)/src/tokenize.o
+
+$(OBJDIR_RELEASE)/src/string.o: src/string.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/string.c -o $(OBJDIR_RELEASE)/src/string.o
+
+$(OBJDIR_RELEASE)/src/stmt.o: src/stmt.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/stmt.c -o $(OBJDIR_RELEASE)/src/stmt.o
+
+$(OBJDIR_RELEASE)/src/query.o: src/query.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/query.c -o $(OBJDIR_RELEASE)/src/query.o
+
+$(OBJDIR_RELEASE)/src/pragma.o: src/pragma.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/pragma.c -o $(OBJDIR_RELEASE)/src/pragma.o
+
+$(OBJDIR_RELEASE)/src/complete.o: src/complete.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/complete.c -o $(OBJDIR_RELEASE)/src/complete.o
+
+$(OBJDIR_RELEASE)/src/parse.o: src/parse.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/parse.c -o $(OBJDIR_RELEASE)/src/parse.o
+
+$(OBJDIR_RELEASE)/src/memory.o: src/memory.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/memory.c -o $(OBJDIR_RELEASE)/src/memory.o
+
+$(OBJDIR_RELEASE)/src/json.o: src/json.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/json.c -o $(OBJDIR_RELEASE)/src/json.o
+
+$(OBJDIR_RELEASE)/src/func.o: src/func.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/func.c -o $(OBJDIR_RELEASE)/src/func.o
+
+$(OBJDIR_RELEASE)/src/expr.o: src/expr.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/expr.c -o $(OBJDIR_RELEASE)/src/expr.o
+
+$(OBJDIR_RELEASE)/src/delete.o: src/delete.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/delete.c -o $(OBJDIR_RELEASE)/src/delete.o
+
+$(OBJDIR_RELEASE)/src/datasrc.o: src/datasrc.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/datasrc.c -o $(OBJDIR_RELEASE)/src/datasrc.o
+
+$(OBJDIR_RELEASE)/src/context.o: src/context.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/context.c -o $(OBJDIR_RELEASE)/src/context.o
+
+$(OBJDIR_RELEASE)/src/conn.o: src/conn.c
+	$(CC) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/conn.c -o $(OBJDIR_RELEASE)/src/conn.o
+
+clean_release: 
+	rm -f $(OBJ_RELEASE) $(OUT_RELEASE)
+	rm -rf $(OBJDIR_RELEASE)/src
+
+.PHONY: before_debug after_debug clean_debug before_release after_release clean_release
+

+ 89 - 0
unql/src/complete.c

@@ -0,0 +1,89 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code that implements the xjd1_complete() API.
+*/
+#include "xjd1Int.h"
+
+/*
+** Return true if the statement in zStmt is complete.
+**
+** A complete statement ends with a semicolon where the semicolon is
+** not part of a larger token such as a string or a JSON structure.
+**
+** Trailing whitespace is ignored.  Comment text is considered whitespace
+** for the purposes of the previous sentence.
+**
+** An empty string is considered a complete statement.
+*/
+int xjd1_complete(const char *zStmt){
+  int isComplete = 1;
+  if( zStmt==0 ) return 1;
+  while( zStmt[0] ){
+    switch( zStmt[0] ){
+      case ';': {
+        isComplete = 1;
+        zStmt++;
+        break;
+      }
+      case ' ':
+      case '\r':
+      case '\t':
+      case '\n':
+      case '\f': {  /* White space is ignored */
+        zStmt++;
+        break;
+      }
+      case '/': {   /* C-style comments */
+        if( zStmt[1]!='*' ){
+          zStmt++;
+          break;
+        }
+        zStmt += 2;
+        while( zStmt[0] && (zStmt[0]!='*' || zStmt[1]!='/') ){ zStmt++; }
+        if( zStmt[0]==0 ) return 0;
+        zStmt++;
+        break;
+      }
+      case '-': {   /* SQL-style comments from "--" to end of line */
+        if( zStmt[1]!='-' ){
+          zStmt++;
+          break;
+        }
+        while( zStmt[0] && *zStmt!='\n' ){ zStmt++; }
+        if( zStmt[0]==0 ) return isComplete;
+        break;
+      }
+      case '"': {   /* C-style string literals */
+        zStmt++;
+        while( zStmt[0] && zStmt[0]!='"' ){
+          if( zStmt[0]=='\\' ) zStmt++;
+          zStmt++;
+        }
+        if( zStmt[0]==0 ) return 0;
+        zStmt++;
+        isComplete = 0;
+        break;
+      }
+      default: {    /* Anything else */
+        zStmt++;
+        isComplete = 0;
+        break;
+      }
+    }
+  }
+  return isComplete;
+}

+ 148 - 0
unql/src/conn.c

@@ -0,0 +1,148 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Main interfaces
+*/
+#include "xjd1Int.h"
+
+/*
+** Open a new database connection
+*/
+int xjd1_open_with_db(xjd1_context *pContext, sqlite3 *db, xjd1 **ppNewConn){
+  xjd1 *pConn;
+
+  *ppNewConn = pConn = xjd1_malloc( sizeof(*pConn) );
+  if( pConn==0 ) return XJD1_NOMEM;
+  memset(pConn, 0, sizeof(*pConn));
+  pConn->pContext = pContext;
+  pConn->db = db;
+  pConn->isSQLite3Borrowed = 1;
+  return XJD1_OK;
+}
+
+int xjd1_open(xjd1_context *pContext, const char *zURI, xjd1 **ppNewConn){
+  sqlite3 *db;
+  int rc;
+
+  rc = sqlite3_open_v2(zURI, &db,
+            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, 0);
+  if( rc ){
+    sqlite3_close(db);
+    return XJD1_ERROR_OPEN_DB;
+  }
+  rc = xjd1_open_with_db(pContext, db, ppNewConn);
+  if(rc != XJD1_OK){
+    sqlite3_close(db);
+    return rc;
+  }
+  (*ppNewConn)->isSQLite3Borrowed = 0;
+  return rc;
+}
+
+
+/* Configure a database connection */
+int xjd1_config(xjd1 *pConn, int op, ...){
+  int rc = XJD1_UNKNOWN;
+  va_list ap;
+  va_start(ap, op);
+  switch( op ){
+    case XJD1_CONFIG_PARSERTRACE: {
+      pConn->parserTrace = va_arg(ap, int);
+      rc = XJD1_OK;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+  va_end(ap);
+  return rc;
+}
+
+/* Close a database connection.  The close does not actually
+** occur until all references to the connection also close.  This
+** means that any prepared statements must also be closed.
+*/
+int xjd1_close(xjd1 *pConn){
+  if( pConn==0 ) return XJD1_OK;
+  pConn->isDying = 1;
+  if( pConn->nRef>0 ) return XJD1_OK;
+  xjd1ContextUnref(pConn->pContext);
+  if(!pConn->isSQLite3Borrowed) sqlite3_close(pConn->db);
+  xjd1StringClear(&pConn->errMsg);
+  xjd1_free(pConn);
+  return XJD1_OK;
+}
+
+/*
+** Report the most recent error.
+*/
+int xjd1_errcode(xjd1 *pConn){
+  return pConn ? pConn->errCode : XJD1_ERROR;
+}
+const char *xjd1_errmsg(xjd1 *pConn){
+  if( pConn==0 ) return "out of memory";
+  return xjd1StringText(&pConn->errMsg);
+}
+
+const char *xjd1_errcode_name(xjd1 *pConn){
+  const char *z = "???";
+  switch( xjd1_errcode(pConn) ){
+    case XJD1_OK:        z = "OK";         break;
+    case XJD1_ERROR:     z = "ERROR";      break;
+    case XJD1_MISUSE:    z = "MISUSE";     break;
+    case XJD1_NOMEM:     z = "NOMEM";      break;
+    case XJD1_UNKNOWN:   z = "UNKNOWN";    break;
+    case XJD1_SYNTAX:    z = "SYNTAX";     break;
+    case XJD1_ROW:       z = "ROW";        break;
+    case XJD1_DONE:      z = "DONE";       break;
+  }
+  return z;
+}
+
+/* Remove a reference to a database connection.  When the last reference
+** is removed and the database is closed, then memory is deallocated.
+*/
+PRIVATE void xjd1Unref(xjd1 *pConn){
+  pConn->nRef--;
+  if( pConn->nRef<=0 && pConn->isDying ) xjd1_close(pConn);
+}
+
+/*
+** Change the error message and error code.
+*/
+PRIVATE void xjd1Error(xjd1 *pConn, int errCode, const char *zFormat, ...){
+  va_list ap;
+  if( pConn==0 ) return;
+  if( !pConn->appendErr ){
+    xjd1StringTruncate(&pConn->errMsg);
+  }else if( xjd1StringLen(&pConn->errMsg) ){
+    xjd1StringAppend(&pConn->errMsg, "\n", 1);
+  }
+  pConn->errCode = errCode;
+  if( zFormat ){
+    va_start(ap, zFormat);
+    xjd1StringVAppendF(&pConn->errMsg, zFormat, ap);
+    va_end(ap);
+  }else{
+    const char *z;
+    switch( errCode ){
+      case XJD1_NOMEM: z = "out of memory";          break;
+      default:         z = xjd1_errcode_name(pConn); break;
+    }
+    xjd1StringAppend(&pConn->errMsg, z, -1);
+  }
+}

+ 48 - 0
unql/src/context.c

@@ -0,0 +1,48 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Execution context
+*/
+#include "xjd1Int.h"
+
+
+int xjd1_context_new(xjd1_context **ppNew){
+  xjd1_context *p;
+
+  *ppNew = p = xjd1_malloc( sizeof(*p) );
+  if( p ){
+    memset(p, 0, sizeof(*p));
+    return XJD1_OK;
+  }else{
+    return XJD1_NOMEM;
+  }
+}
+int xjd1_context_config(xjd1_context *p, int op, ...){
+  return XJD1_UNKNOWN;
+}
+int xjd1_context_delete(xjd1_context *p){
+  if( p==0 ) return XJD1_OK;
+  p->isDying = 1;
+  if( p->nRef>0 ) return XJD1_OK;
+  xjd1_free(p);
+  return XJD1_OK;
+}
+
+PRIVATE void xjd1ContextUnref(xjd1_context *p){
+  if( p==0 ) return;
+  p->nRef--;
+  if( p->nRef<=0 && p->isDying ) xjd1_context_delete(p);
+}

+ 634 - 0
unql/src/datasrc.c

@@ -0,0 +1,634 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code used to implement methods for the DataSrc object.
+*/
+#include "xjd1Int.h"
+
+
+/*
+** Called after statement parsing to initalize every DataSrc object.
+*/
+int xjd1DataSrcInit(DataSrc *p, Query *pQuery, void *pOuterCtx){
+  int rc = XJD1_OK;
+  p->pQuery = pQuery;
+  switch( p->eDSType ){
+    case TK_COMMA: {
+      xjd1DataSrcInit(p->u.join.pLeft, pQuery, pOuterCtx);
+      xjd1DataSrcInit(p->u.join.pRight, pQuery, pOuterCtx);
+      break;
+    }
+    case TK_SELECT: {
+      xjd1QueryInit(p->u.subq.q, pQuery->pStmt, 0);
+      break;
+    }
+    case TK_ID: {
+      char *zSql = sqlite3_mprintf("SELECT x FROM \"%w\"", p->u.tab.zName);
+//printf("\n%d:%s:%s\n", __LINE__, __FILE__, zSql);
+      sqlite3_prepare_v2(pQuery->pStmt->pConn->db, zSql, -1,
+                         &p->u.tab.pStmt, 0);
+      sqlite3_free(zSql);
+      break;
+    }
+    case TK_FLATTENOP: {
+      xjd1DataSrcInit(p->u.flatten.pNext, pQuery, pOuterCtx);
+      break;
+    }
+    case TK_DOT: {
+      rc = xjd1ExprInit(p->u.path.pPath, pQuery->pStmt, 0, 0, pOuterCtx);
+      break;
+    }
+    case TK_NULL:                 /* Initializing a NULL DS is a no-op */
+      assert( p->u.null.isDone==0 );
+      break;
+  }
+  return rc;
+}
+
+static JsonNode *newIntValue(int i){
+  JsonNode *pRet = xjd1JsonNew(0);
+  pRet->eJType = XJD1_REAL;
+  pRet->u.r = (double)i;
+  return pRet;
+}
+
+static JsonNode *newStringValue(const char *z){
+  JsonNode *pRet = xjd1JsonNew(0);
+  pRet->eJType = XJD1_STRING;
+  pRet->u.z = xjd1PoolDup(0, z, -1);
+  return pRet;
+}
+
+/*
+** Argument pPath is a an expression consisting entirely of TK_ID
+** and TK_DOT noddes. e.g. "a.b.c.d". If value pVal contains the
+** identified property, a pointer to the JsonStructElem that contains
+** it is returned.
+**
+** If bCreate is not true and object pVal does not contain the specified
+** property, NULL is returned. Or, if bCreate is true and pVal contains all
+** but the rightmost component of the path, a new element is added to the
+** object and a pointer to it returned.
+*/
+static JsonStructElem *findStructElem(JsonNode *pVal, Expr *pPath, int bCreate){
+  JsonStructElem *pRet = 0;       /* Return value */
+
+  assert( bCreate==0 || pVal->nRef==1 );
+  assert( pPath->eType==TK_ID || pPath->eType==TK_DOT );
+
+  if( pVal && pVal->eJType==XJD1_STRUCT ){
+    JsonNode *p = 0;
+    const char *zAs;
+    if( pPath->eType==TK_DOT ){
+      JsonStructElem *pElem;
+      pElem = findStructElem(pVal, pPath->u.lvalue.pLeft, bCreate);
+      if( pElem ){
+        p = pElem->pValue;
+        zAs = pPath->u.lvalue.zId;
+      }
+    }else{
+      p = pVal;
+      zAs = pPath->u.id.zId;
+    }
+
+    if( p && p->eJType==XJD1_STRUCT ){
+      for(pRet=p->u.st.pFirst; pRet; pRet=pRet->pNext){
+        if( 0==strcmp(pRet->zLabel, zAs) ) break;
+      }
+      if( pRet==0 && bCreate ){
+        pRet = xjd1MallocZero(sizeof(*pRet));
+        pRet->zLabel = xjd1PoolDup(0, zAs, -1);
+        if( p->u.st.pLast ){
+          p->u.st.pLast->pNext = pRet;
+        }else{
+          p->u.st.pFirst = pRet;
+        }
+        p->u.st.pLast = pRet;
+      }
+    }
+  }
+
+  return pRet;
+}
+
+/*
+** An iterator of this type is used by FLATTEN and EACH datasources. It
+** is allocated, accessed and deleted only by the functions:
+**
+**     flattenIterNew()
+**     flattenIterNext()
+**     flattenIterEntry()
+**     flattenIterFree()
+*/
+struct FlattenIter {
+  int nAlloc;                     /* Allocated size of aIter[] array */
+  int nIter;                      /* Number of aIter[] array elements in use */
+  int isRecursive;                /* True for FLATTEN, false for EACH */
+  struct FlattenIterElem {
+    JsonNode *pVal;               /* Struct or List to iterate through. */
+    union {
+      JsonStructElem *pElem;      /* If pVal->eJType==XJD1_STRUCT */
+      int iElem;                  /* If pVal->eJType==XJD1_ARRAY */
+    } current;
+  } aIter[1];
+};
+
+/*
+** Allocate a new iterator to iterate through value pVal. If parameter
+** isRecursive is true, then the iteration descends into any contained structs
+** or arrays (for the FLATTEN operator). Otherwise, the iteration is not
+** recursive (used by the EACH operator).
+*/
+static FlattenIter *flattenIterNew(
+  JsonNode *pBase,                /* Base object */
+  Expr *pPath,                    /* Path to flatten or each on */
+  int isRecursive                 /* True for FLATTEN, false for EACH */
+){
+  FlattenIter *pNew = 0;          /* New iterator object */
+  JsonStructElem *pElem;
+
+  pElem = findStructElem(pBase, pPath, 0);
+  if( pElem ){
+    JsonNode *pVal = pElem->pValue;
+    if( pVal->eJType==XJD1_STRUCT || pVal->eJType==XJD1_ARRAY ){
+      pNew = (FlattenIter *)xjd1MallocZero(sizeof(FlattenIter));
+      if( pNew ){
+        pNew->nAlloc = 1;
+        pNew->nIter = 1;
+        pNew->isRecursive = isRecursive;
+        pNew->aIter[0].pVal = xjd1JsonRef(pVal);
+      }
+    }
+  }
+
+  return pNew;
+}
+
+static int flattenIterEntry(
+  FlattenIter *pIter,             /* Iterator handle */
+  JsonNode **ppKey,               /* OUT: Current key value */
+  JsonNode **ppVal                /* OUT: Current payload value */
+){
+  struct FlattenIterElem *p = &pIter->aIter[pIter->nIter-1];
+
+  assert( p->pVal->eJType==XJD1_STRUCT || p->pVal->eJType==XJD1_ARRAY );
+  if( p->pVal->eJType==XJD1_STRUCT ){
+    *ppVal = xjd1JsonRef(p->current.pElem->pValue);
+  }else{
+    *ppVal = xjd1JsonRef(p->pVal->u.ar.apElem[p->current.iElem-1]);
+  }
+
+  if( ppKey ){
+    JsonNode *pKey = 0;
+    JsonNode *pList = 0;
+    int i;
+
+    if( pIter->isRecursive ){
+      pList = xjd1JsonNew(0);
+      pList->eJType = XJD1_ARRAY;
+      pList->u.ar.nElem = pIter->nIter;
+      pList->u.ar.apElem = xjd1MallocZero(pIter->nIter*sizeof(JsonNode *));
+    }
+
+    for(i=pIter->nIter-1; i>=0; i--){
+      p = &pIter->aIter[i];
+      if( p->pVal->eJType==XJD1_STRUCT ){
+        pKey = newStringValue(p->current.pElem->zLabel);
+      }else{
+        pKey = newIntValue(p->current.iElem-1);
+      }
+      if( pIter->isRecursive==0 ) break;
+      pList->u.ar.apElem[i] = pKey;
+    }
+
+    *ppKey = (pIter->isRecursive ? pList : pKey);
+  }
+  return XJD1_OK;
+}
+
+static int flattenIterNext(FlattenIter **ppIter){
+  FlattenIter *pIter = *ppIter;
+  int rc = XJD1_DONE;
+  if( pIter ){
+    while( rc==XJD1_DONE && pIter->nIter ){
+      struct FlattenIterElem *p = &(*ppIter)->aIter[pIter->nIter-1];
+      if( p->pVal->eJType==XJD1_STRUCT ){
+        if( p->current.pElem==0 ){
+          p->current.pElem = p->pVal->u.st.pFirst;
+        }else{
+          p->current.pElem = p->current.pElem->pNext;
+        }
+        if( p->current.pElem ){
+          rc = XJD1_ROW;
+        }else{
+          pIter->nIter--;
+        }
+      }else{
+        assert( p->pVal->eJType==XJD1_ARRAY );
+        p->current.iElem++;
+        if( p->current.iElem<=p->pVal->u.ar.nElem ){
+          rc = XJD1_ROW;
+        }else{
+          pIter->nIter--;
+        }
+      }
+
+      if( pIter->isRecursive && rc==XJD1_ROW ){
+        JsonNode *pVal;
+        flattenIterEntry(pIter, 0, &pVal);
+        if( pVal->eJType==XJD1_STRUCT || pVal->eJType==XJD1_ARRAY ){
+          assert( pIter->nIter<=pIter->nAlloc );
+          if( pIter->nIter==pIter->nAlloc ){
+            int nNew = sizeof(FlattenIter) + (pIter->nAlloc*2-1)*sizeof(*p);
+            FlattenIter *pNew;
+
+            pNew = (FlattenIter *)xjd1_realloc(pIter, nNew);
+            pNew->nAlloc = pNew->nAlloc * 2;
+            *ppIter = pNew;
+            pIter = pNew;
+          }
+
+          pIter->aIter[pIter->nIter].pVal = pVal;
+          pIter->aIter[pIter->nIter].current.pElem = 0;
+          pIter->aIter[pIter->nIter].current.iElem = 0;
+          pIter->nIter++;
+          rc = XJD1_DONE;
+        }
+      }
+    }
+  }
+  return rc;
+}
+
+/*
+** Free an iterator allocated by flattenIterNew().
+*/
+static void flattenIterFree(FlattenIter *pIter){
+  if( pIter ){
+    int i;
+    for(i=0; i<pIter->nIter; i++){
+      xjd1JsonFree(pIter->aIter[i].pVal);
+    }
+    xjd1_free(pIter);
+  }
+}
+
+
+
+/*
+** This function makes a copy of the JSON value (type XJD1_STRUCT) passed as
+** the first object, adds a property to it, and returns a pointer to the copy.
+** The ref-count of the returned object is 1.
+**
+** The name of the property added is passed as the zAs argument. The property
+** is set to a structure containing two fields, "k" (value pKey) and "v"
+** (value pValue). i.e. if the arguments are as follows:
+**
+**     pBase   = {a:a, b:b}
+**     pKey    = "abc"
+**     pValue  = "xyz"
+**     zAs     = "c"
+**
+** then the returned object is:
+**
+**     {a:a, b:b, c:{k:"abc", v:"xyz"}}
+**
+** If object pBase already has a field named "zAs", then it is replaced in
+** the returned copy.
+**
+** This function decrements the ref-count of arguments pKey and pValue. But
+** not pBase.
+*/
+static JsonNode *flattenedObject(
+  JsonNode *pBase,                /* Base object */
+  JsonNode *pKey,
+  JsonNode *pValue,
+  Expr *pPath
+){
+  JsonNode *pRet;                 /* Value to return */
+  JsonNode *pKV;                  /* New value for property zAs */
+  JsonStructElem *pElem;
+
+  pKV = xjd1JsonNew(0);
+  pKV->eJType = XJD1_STRUCT;
+  xjd1JsonInsert(pKV, "k", pKey);
+  xjd1JsonInsert(pKV, "v", pValue);
+
+  pRet = xjd1JsonEdit(xjd1JsonRef(pBase));
+  pElem = findStructElem(pRet, pPath, 1);
+  xjd1JsonFree(pElem->pValue);
+  pElem->pValue = pKV;
+
+  assert( pRet->nRef==1 && pRet!=pBase );
+  return pRet;
+}
+
+/*
+** Advance a data source to the next row. Return XJD1_DONE if the data
+** source is at EOF or XJD1_ROW if the step results in a row of content
+** being available.
+*/
+int xjd1DataSrcStep(DataSrc *p){
+  int rc= XJD1_DONE;
+  if( p==0 ) return XJD1_DONE;
+  switch( p->eDSType ){
+    case TK_COMMA: {
+
+      if( p->u.join.bStart==0 ){
+        p->u.join.bStart = 1;
+        rc = xjd1DataSrcStep(p->u.join.pLeft);
+        if( rc!=XJD1_ROW ) break;
+      }
+
+      rc = xjd1DataSrcStep(p->u.join.pRight);
+      if( rc==XJD1_DONE ){
+        rc = xjd1DataSrcStep(p->u.join.pLeft);
+        if( rc==XJD1_ROW ) {
+          xjd1DataSrcRewind(p->u.join.pRight);
+          rc = xjd1DataSrcStep(p->u.join.pRight);
+        }
+      }
+      break;
+    }
+
+    case TK_SELECT: {
+      xjd1JsonFree(p->pValue);
+      p->pValue = 0;
+      rc = xjd1QueryStep(p->u.subq.q);
+      p->pValue = xjd1QueryDoc(p->u.subq.q, 0);
+      break;
+    }
+
+    case TK_ID: {
+      rc = sqlite3_step(p->u.tab.pStmt);
+      xjd1JsonFree(p->pValue);
+      p->pValue = 0;
+      if( rc==SQLITE_ROW ){
+        const char *zJson = (const char*)sqlite3_column_text(p->u.tab.pStmt, 0);
+        p->pValue = xjd1JsonParse(zJson, -1);
+        rc = XJD1_ROW;
+      }else{
+        p->u.tab.eofSeen = 1;
+        rc = XJD1_DONE;
+      }
+      break;
+    }
+
+    case TK_FLATTENOP: {
+      xjd1JsonFree(p->pValue);
+      p->pValue = 0;
+
+      while( XJD1_ROW!=(rc = flattenIterNext(&p->u.flatten.pIter)) ){
+        flattenIterFree(p->u.flatten.pIter);
+        p->u.flatten.pIter = 0;
+
+        rc = xjd1DataSrcStep(p->u.flatten.pNext);
+        if( rc!=XJD1_ROW ){
+          break;
+        }else{
+          int isRecursive = (p->u.flatten.cOpName=='F');
+          JsonNode *pBase = p->u.flatten.pNext->pValue;
+          Expr *pPath = p->u.flatten.pExpr;
+
+          p->u.flatten.pIter = flattenIterNew(pBase, pPath, isRecursive);
+        }
+      }
+
+      if( rc==XJD1_ROW ){
+        JsonNode *pKey = 0;
+        JsonNode *pValue = 0;
+        JsonNode *pBase = p->u.flatten.pNext->pValue;
+        flattenIterEntry(p->u.flatten.pIter, &pKey, &pValue);
+        p->pValue = flattenedObject(pBase, pKey, pValue, p->u.flatten.pAs);
+      }
+
+      break;
+    }
+
+    case TK_DOT: {
+      JsonNode *pArray = p->u.path.pArray;
+
+      xjd1JsonFree(p->pValue);
+      p->pValue = 0;
+
+      if( pArray==0 ){
+        pArray = p->u.path.pArray = xjd1ExprEval(p->u.path.pPath);
+      }
+      if( pArray
+       && pArray->eJType==XJD1_ARRAY
+       && p->u.path.iNext<pArray->u.ar.nElem
+      ){
+        rc = XJD1_ROW;
+        p->pValue = xjd1JsonRef(pArray->u.ar.apElem[p->u.path.iNext++]);
+      }
+
+      break;
+    }
+
+    case TK_NULL: {
+      rc = (p->u.null.isDone ? XJD1_DONE : XJD1_ROW);
+      p->u.null.isDone = 1;
+      break;
+    }
+  }
+  return rc;
+}
+
+/*
+** Return the document that this data source is current pointing to
+** if the AS name of the document is zDocName or if zDocName==0.
+** The AS name is important since a join might have multiple documents.
+**
+** xjd1JsonRef() has been called on the returned string.  The caller
+** must invoke xjd1JsonFree().
+*/
+JsonNode *xjd1DataSrcDoc(DataSrc *p, const char *zDocName){
+  JsonNode *pRes = 0;
+  if( p==0 ) return 0;
+  if( zDocName && p->zAs && 0==strcmp(p->zAs, zDocName) ){
+    return xjd1JsonRef(p->pValue);
+  }
+  switch( p->eDSType ){
+    case TK_COMMA: {
+      pRes = xjd1DataSrcDoc(p->u.join.pLeft, zDocName);
+      if( pRes==0 ) pRes = xjd1DataSrcDoc(p->u.join.pRight, zDocName);
+      break;
+    }
+    case TK_SELECT: {
+      assert( p->zAs );
+      if( zDocName==0 ){
+        pRes = xjd1JsonRef(p->pValue);
+      }
+      break;
+    }
+    case TK_FLATTENOP:
+    case TK_ID: {
+      if( zDocName==0 || (p->zAs==0 && strcmp(p->u.tab.zName, zDocName)==0) ){
+        pRes = xjd1JsonRef(p->pValue);
+      }
+      break;
+    }
+    default: {
+      pRes = xjd1JsonRef(p->pValue);
+      break;
+    }
+  }
+  return pRes;
+}
+
+/*
+** Rewind a data source so that the next call to DataSrcStep() will cause
+** it to point to the first row.
+*/
+int xjd1DataSrcRewind(DataSrc *p){
+  if( p==0 ) return XJD1_DONE;
+  xjd1JsonFree(p->pValue);  p->pValue = 0;
+  switch( p->eDSType ){
+    case TK_COMMA: {
+      p->u.join.bStart = 0;
+      xjd1DataSrcRewind(p->u.join.pLeft);
+      xjd1DataSrcRewind(p->u.join.pRight);
+      break;
+    }
+    case TK_SELECT: {
+      xjd1QueryRewind(p->u.subq.q);
+      break;
+    }
+    case TK_ID: {
+      sqlite3_reset(p->u.tab.pStmt);
+      break;
+    }
+    case TK_DOT: {
+      xjd1JsonFree(p->u.path.pArray);
+      p->u.path.pArray = 0;
+      p->u.path.iNext = 0;
+      break;
+    }
+    case TK_FLATTENOP: {
+      xjd1DataSrcRewind(p->u.flatten.pNext);
+      flattenIterFree(p->u.flatten.pIter);
+      p->u.flatten.pIter = 0;
+      break;
+    }
+    case TK_NULL: {
+      p->u.null.isDone = 0;
+      break;
+    }
+  }
+  return XJD1_DONE;
+}
+
+/*
+** The destructor for a Query object.
+*/
+int xjd1DataSrcClose(DataSrc *p){
+  if( p==0 ) return XJD1_OK;
+  xjd1JsonFree(p->pValue);  p->pValue = 0;
+  switch( p->eDSType ){
+    case TK_ID: {
+//printf("\n%d:%s:%s\n", __LINE__, __FILE__, sqlite3_sql(p->u.tab.pStmt));
+      sqlite3_finalize(p->u.tab.pStmt);
+      break;
+    }
+    case TK_FLATTENOP: {
+      xjd1DataSrcClose(p->u.flatten.pNext);
+      break;
+    }
+    case TK_DOT: {
+      xjd1JsonFree(p->u.path.pArray);
+      p->u.path.pArray = 0;
+      break;
+    }
+  }
+  return XJD1_OK;
+}
+
+int xjd1DataSrcCount(DataSrc *p){
+  int n = 1;
+  if( p->eDSType==TK_COMMA ){
+    n = xjd1DataSrcCount(p->u.join.pLeft) + xjd1DataSrcCount(p->u.join.pRight);
+  }
+  return n;
+}
+
+static void cacheSaveRecursive(DataSrc *p, JsonNode ***papNode){
+  if( p->eDSType==TK_COMMA ){
+    cacheSaveRecursive(p->u.join.pLeft, papNode);
+    cacheSaveRecursive(p->u.join.pRight, papNode);
+  }else{
+    xjd1JsonFree(**papNode);
+    **papNode = xjd1JsonRef(p->pValue);
+    (*papNode)++;
+  }
+}
+
+void xjd1DataSrcCacheSave(DataSrc *p, JsonNode **apNode){
+  JsonNode **pp = apNode;
+  cacheSaveRecursive(p, &pp);
+}
+
+static int datasrcResolveRecursive(
+  DataSrc *p,
+  int *piEntry,
+  const char *zDocname
+){
+  int ret = 0;
+  if( p->eDSType==TK_COMMA ){
+    ret = datasrcResolveRecursive(p->u.join.pLeft, piEntry, zDocname);
+    if( 0==ret ){
+      ret = datasrcResolveRecursive(p->u.join.pRight, piEntry, zDocname);
+    }
+  }else if( p->eDSType==TK_FLATTENOP ){
+    ret = datasrcResolveRecursive(p->u.flatten.pNext, piEntry, zDocname);
+  }else{
+    if( (p->zAs && 0==strcmp(zDocname, p->zAs))
+     || (p->zAs==0 && p->eDSType==TK_ID && strcmp(p->u.tab.zName, zDocname)==0)
+    ){
+      ret = *piEntry;
+    }
+    (*piEntry)++;
+  }
+  return ret;
+}
+int xjd1DataSrcResolve(DataSrc *p, const char *zDocname){
+  int iEntry = 1;
+  return datasrcResolveRecursive(p, &iEntry, zDocname);
+}
+
+static JsonNode *datasrcReadRecursive(
+  DataSrc *p,
+  int *piEntry,
+  int iDoc
+){
+  JsonNode *pRet = 0;
+  if( p->eDSType==TK_COMMA ){
+    pRet = datasrcReadRecursive(p->u.join.pLeft, piEntry, iDoc);
+    if( 0==pRet ){
+      pRet = datasrcReadRecursive(p->u.join.pRight, piEntry, iDoc);
+    }
+  }else{
+    if( *piEntry==iDoc ){
+      pRet = xjd1JsonRef(p->pValue);
+    }
+    (*piEntry)++;
+  }
+  return pRet;
+}
+JsonNode *xjd1DataSrcRead(DataSrc *p, int iDoc){
+  int iEntry = 1;
+  assert( iDoc>=1 );
+  return datasrcReadRecursive(p, &iEntry, iDoc);
+}

+ 75 - 0
unql/src/delete.c

@@ -0,0 +1,75 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Code to evaluate a DELETE command.
+*/
+#include "xjd1Int.h"
+
+/*
+** Evaluate a pragma.
+**
+** Unknown pragmas are silently ignored.
+*/
+int xjd1DeleteStep(xjd1_stmt *pStmt){
+  Command *pCmd = pStmt->pCmd;
+  int rc = XJD1_OK;
+  int inAutocommit;
+  sqlite3 *db;
+  sqlite3_stmt *pQuery = 0;
+  sqlite3_stmt *pIns = 0;
+  char *zSql;
+
+  assert( pCmd!=0 );
+  assert( pCmd->eCmdType==TK_DELETE );
+  db = pStmt->pConn->db;
+  inAutocommit = sqlite3_get_autocommit(db);
+  if( pCmd->u.del.pWhere==0 ){
+    zSql = sqlite3_mprintf("DELETE FROM \"%w\"", pCmd->u.del.zName);
+    sqlite3_exec(db, zSql, 0, 0, 0);
+    sqlite3_free(zSql);
+    return XJD1_OK;
+  }
+  zSql = sqlite3_mprintf("%sCREATE TEMP TABLE _t1(x INTEGER PRIMARY KEY)",
+            inAutocommit ? "BEGIN;" : "");
+  sqlite3_exec(db, zSql, 0, 0, 0);
+  sqlite3_free(zSql);
+  zSql = sqlite3_mprintf("SELECT rowid, x FROM \"%w\"", pCmd->u.del.zName);
+  sqlite3_prepare_v2(db, zSql, -1, &pQuery, 0);
+  sqlite3_prepare_v2(db, "INSERT INTO _t1(x) VALUES(?1)", -1, &pIns, 0);
+  if( pQuery ){
+    while( SQLITE_ROW==sqlite3_step(pQuery) ){
+      const char *zJson = (const char*)sqlite3_column_text(pQuery, 1);
+      pStmt->pDoc = xjd1JsonParse(zJson, -1);
+      if( xjd1ExprTrue(pCmd->u.del.pWhere) ){
+        sqlite3_bind_int64(pIns, 1, sqlite3_column_int64(pQuery, 0));
+        sqlite3_step(pIns);
+        sqlite3_reset(pIns);
+      }
+      xjd1JsonFree(pStmt->pDoc);
+      pStmt->pDoc = 0;
+    }
+  }
+  sqlite3_finalize(pQuery);
+  sqlite3_finalize(pIns);
+  sqlite3_free(zSql);
+  zSql = sqlite3_mprintf(
+            "DELETE FROM \"%w\" WHERE rowid IN _t1;"
+            "DROP TABLE _t1;%s", pCmd->u.del.zName,
+            inAutocommit ? "COMMIT;" : "");
+  sqlite3_exec(db, zSql, 0, 0, 0);
+  sqlite3_free(zSql);
+  return rc;
+}

+ 843 - 0
unql/src/expr.c

@@ -0,0 +1,843 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code used to evaluate expressions at run-time.
+*/
+#include "xjd1Int.h"
+#include <math.h>
+
+
+/*
+** Instances of the following structure are used when resolving document
+** references in expressions.
+*/
+typedef struct ResolveCtx ResolveCtx;
+struct ResolveCtx {
+  xjd1_stmt *pStmt;               /* Statement expressions are part of */
+  Query *pQuery;                  /* Query expressions are part of */
+  int eExpr;                      /* Role of expression in pQuery */
+  ResolveCtx *pParent;            /* NULL or parent of pQuery */
+};
+
+/* forward reference */
+static int walkExpr(Expr*, int (*)(Expr *,void *), void *);
+
+/*
+** Walk an expression list
+*/
+static int walkExprList(ExprList *p, int (*xFunc)(Expr *,void *), void *pCtx){
+  if( p ){
+    int i;
+    for(i=0; i<p->nEItem; i++){
+      walkExpr(p->apEItem[i].pExpr, xFunc, pCtx);
+    }
+  }
+  return XJD1_OK;
+}
+
+/*
+** Walk an expression tree
+*/
+static int walkExpr(Expr *p, int (*xFunc)(Expr *,void *), void *pCtx){
+  int rc = XJD1_OK;
+  if( p==0 ) return XJD1_OK;
+  rc = xFunc(p, pCtx);
+  switch( p->eClass ){
+    case XJD1_EXPR_BI: {
+      walkExpr(p->u.bi.pLeft, xFunc, pCtx);
+      walkExpr(p->u.bi.pRight, xFunc, pCtx);
+      break;
+    }
+    case XJD1_EXPR_TRI: {
+      walkExpr(p->u.tri.pTest, xFunc, pCtx);
+      walkExpr(p->u.tri.pIfTrue, xFunc, pCtx);
+      walkExpr(p->u.tri.pIfFalse, xFunc, pCtx);
+      break;
+    }
+    case XJD1_EXPR_TK: {
+      /* Nothing to do */
+      break;
+    }
+    case XJD1_EXPR_FUNC: {
+      walkExprList(p->u.func.args, xFunc, pCtx);
+      break;
+    }
+    case XJD1_EXPR_JSON: {
+      /* Nothing to do */
+      break;
+    }
+    case XJD1_EXPR_ARRAY: {
+      walkExprList(p->u.ar, xFunc, pCtx);
+      break;
+    }
+    case XJD1_EXPR_STRUCT: {
+      walkExprList(p->u.st, xFunc, pCtx);
+      break;
+    }
+    case XJD1_EXPR_LVALUE: {
+      walkExpr(p->u.lvalue.pLeft, xFunc, pCtx);
+      break;
+    }
+  }
+  return rc;
+}
+
+static int exprResolve(Expr *p, ResolveCtx *pCtx){
+  Command *pCmd = pCtx->pStmt->pCmd;
+  const char *zDoc;
+
+  zDoc = p->u.id.zId;
+  switch( pCmd->eCmdType ){
+    case TK_DELETE:
+      if( 0==strcmp(zDoc, pCmd->u.del.zName) ) return XJD1_OK;
+      break;
+
+    case TK_UPDATE:
+      if( 0==strcmp(zDoc, pCmd->u.update.zName) ) return XJD1_OK;
+      break;
+
+    case TK_SELECT: {
+      ResolveCtx *pTest;
+      for(pTest=pCtx; pTest; pTest=pTest->pParent){
+        Query *pQuery = pTest->pQuery;
+        if( pQuery ){
+          int eExpr = pTest->eExpr;
+          int bFound = 0;
+
+          assert( pQuery->eQType==TK_SELECT || eExpr==XJD1_EXPR_ORDERBY );
+          assert( eExpr>0 );
+
+          /* Search the FROM clause. */
+          if( pQuery->eQType==TK_SELECT && (
+                eExpr==XJD1_EXPR_RESULT  || eExpr==XJD1_EXPR_WHERE ||
+                eExpr==XJD1_EXPR_GROUPBY || eExpr==XJD1_EXPR_HAVING ||
+                eExpr==XJD1_EXPR_ORDERBY
+                )){
+            int iDatasrc = xjd1DataSrcResolve(pQuery->u.simple.pFrom, zDoc);
+            if( iDatasrc ){
+              p->u.id.iDatasrc = iDatasrc;
+              bFound = 1;
+            }
+          }
+
+          /* Match against any 'AS' alias on the query result */
+          if( bFound==0 && pQuery->zAs ){
+            if( eExpr==XJD1_EXPR_ORDERBY
+                || eExpr==XJD1_EXPR_HAVING
+                || (eExpr==XJD1_EXPR_WHERE && pQuery->u.simple.pAgg==0)
+              ){
+              if( 0==strcmp(zDoc, pQuery->zAs) ){
+                bFound = 1;
+              }
+            }
+          }
+
+          if( bFound ){
+            p->u.id.pQuery = pQuery;
+            return XJD1_OK;
+          }
+        }
+      }
+      break;
+    }
+
+    default:
+      assert( 0 );
+      break;
+  }
+
+  xjd1StmtError(pCtx->pStmt, XJD1_ERROR, "no such object: %s", zDoc);
+  return XJD1_ERROR;
+}
+
+/*
+** Callback for query expressions
+*/
+static int walkInitCallback(Expr *p, void *pArg){
+  int rc = XJD1_OK;
+  ResolveCtx *pCtx = (ResolveCtx *)pArg;
+  assert( p );
+  p->pStmt = pCtx->pStmt;
+  p->pQuery = pCtx->pQuery;
+  switch( p->eClass ){
+    case XJD1_EXPR_Q:
+      rc = xjd1QueryInit(p->u.subq.p, pCtx->pStmt, pArg);
+      break;
+
+    case XJD1_EXPR_FUNC: {
+      rc = xjd1FunctionInit(p, pCtx->pStmt, pCtx->pQuery, pCtx->eExpr);
+      break;
+    }
+
+    case XJD1_EXPR_TK: {
+      rc = exprResolve(p, pCtx);
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  return rc;
+}
+
+
+/*
+** Initialize an expression in preparation for evaluation of a
+** statement.
+*/
+int xjd1ExprInit(
+  Expr *p,                        /* Expression to initialize */
+  xjd1_stmt *pStmt,               /* Statement expression belongs to */
+  Query *pQuery,                  /* Query expression belongs to (or NULL) */
+  int eExpr,                      /* How the expression features in the query */
+  void *pCtx                      /* Parent resolution context */
+){
+  ResolveCtx sCtx;
+  assert( pQuery==0 || pQuery->pStmt==pStmt );
+  sCtx.pStmt = pStmt;
+  sCtx.pQuery = pQuery;
+  sCtx.eExpr = eExpr;
+  sCtx.pParent = (ResolveCtx *)pCtx;
+  return walkExpr(p, walkInitCallback, (void *)&sCtx);
+}
+
+/*
+** Initialize a list of expression in preparation for evaluation of a
+** statement.
+*/
+int xjd1ExprListInit(
+  ExprList *p,                    /* List of expressions to initialize */
+  xjd1_stmt *pStmt,               /* Statement expressions belong to */
+  Query *pQuery,                  /* Query expressions belong to (or NULL) */
+  int eExpr,                      /* How the expressions feature in the query */
+  void *pCtx                      /* Parent resolution context */
+){
+  ResolveCtx sCtx;
+  assert( pQuery==0 || pQuery->pStmt==pStmt );
+  sCtx.pStmt = pStmt;
+  sCtx.pQuery = pQuery;
+  sCtx.eExpr = eExpr;
+  sCtx.pParent = (ResolveCtx *)pCtx;
+  return walkExprList(p, walkInitCallback, (void *)&sCtx);
+}
+
+
+/* Walker callback for ExprClose() */
+static int walkCloseQueryCallback(Expr *p, void *pCtx){
+  int rc = XJD1_OK;
+  if( p->eType==TK_SELECT ){
+    rc = xjd1QueryClose(p->u.subq.p);
+  }
+  return rc;
+}
+
+/*
+** Close all subqueries in an expression.
+*/
+int xjd1ExprClose(Expr *p){
+  return walkExpr(p, walkCloseQueryCallback, 0);
+}
+
+/*
+** Close all subqueries in an expression list.
+*/
+int xjd1ExprListClose(ExprList *p){
+  return walkExprList(p, walkCloseQueryCallback, 0);
+}
+
+/*
+** Return true if the JSON object is a string
+*/
+static int isStr(const JsonNode *p){
+  if( p==0 ) return 0;
+  switch( p->eJType ){
+    case XJD1_TRUE:
+    case XJD1_FALSE:
+    case XJD1_NULL:
+    case XJD1_REAL:
+      return 0;
+  }
+  return 1;
+}
+
+/*
+** Return non-zero if the JSON object passed should be considered TRUE in a
+** boolean context. For example in the result of a WHERE or HAVING clause.
+**
+** XJD1 uses the same rules as Javascript does to determine which
+** values are considered TRUE:
+**
+**   1. Arrays and objects are always considered true.
+**   2. Strings are false if they are zero bytes in length, otherwise true.
+**   3. Numbers are false if equal to zero, or true otherwise.
+**   4. NULL values are false.
+**   5. "true" and "false" are "true" and "false". Respectively.
+*/
+static int isTrue(const JsonNode *p){
+  int res = 0;                    /* Return value */
+  switch( p->eJType ){
+    case XJD1_REAL:
+      res = p->u.r!=0.0;
+      break;
+
+    case XJD1_STRING:
+      res = p->u.z[0]!='\0';
+      break;
+
+    case XJD1_ARRAY:
+    case XJD1_STRUCT:
+    case XJD1_TRUE:
+      res = 1;
+      break;
+  }
+  assert( res==1 || res==0 );
+  return res;
+}
+
+/*
+** Allocate a NULL JSON object.
+*/
+static JsonNode *nullJson(void){
+  JsonNode *pRes = xjd1JsonNew(0);
+  if( pRes ) pRes->eJType = XJD1_NULL;
+  return pRes;
+}
+
+/*
+** If the JSON node passed as the first argument is of type XJD1_STRUCT,
+** attempt to return a pointer to property zProperty.
+**
+** If zProperty is not defined, or if pStruct is not of type XJD1_STRUCT,
+** return a pointer to a NULL value.
+*/
+static JsonNode *getProperty(JsonNode *pStruct, const char *zProperty){
+  JsonStructElem *pElem;
+  JsonNode *pRes = 0;
+
+  if( pStruct && pStruct->eJType==XJD1_STRUCT ){
+    for(pElem=pStruct->u.st.pFirst; pElem; pElem=pElem->pNext){
+      if( strcmp(pElem->zLabel, zProperty)==0 ){
+        pRes = xjd1JsonRef(pElem->pValue);
+        break;
+      }
+    }
+  }
+
+  if( pRes==0 ){
+    pRes = nullJson();
+  }
+  return pRes;
+}
+
+/*
+** Return TRUE if and only if all of the following are true:
+**
+**   (1)  pA exists
+**   (2)  pB exists and is either an array or a structure
+**   (3)  pA is a label within pB
+*/
+static int inOperator(JsonNode *pA, JsonNode *pB){
+  char *zLHS;
+  int rc = 0;
+  char zBuf[100];
+  if( pA==0 ) return 0;
+  if( pB==0 ) return 0;
+  switch( pA->eJType ){
+    case XJD1_FALSE:
+    case XJD1_TRUE:
+    case XJD1_NULL:
+    case XJD1_ARRAY:
+    case XJD1_STRUCT:
+      return 0;
+    case XJD1_REAL:
+      sqlite3_snprintf(sizeof(zBuf), zBuf, "%g", pA->u.r);
+      zLHS = zBuf;
+      break;
+    case XJD1_STRING:
+      zLHS = pA->u.z;
+      break;
+  }
+  switch( pB->eJType ){
+    case XJD1_ARRAY: {
+      char *zTail;
+      int x = strtol(zLHS, &zTail, 0);
+      if( zTail[0]!=0 ) return 0;
+      rc = (x>=0 && x<pB->u.ar.nElem);
+      break;
+    }
+    case XJD1_STRUCT: {
+      JsonStructElem *p;
+      for(p=pB->u.st.pFirst; p; p=p->pNext){
+        if( strcmp(p->zLabel, zLHS)==0 ){
+          rc = 1;
+          break;
+        }
+      }
+      break;
+    }
+    default: {
+      rc = 0;
+    }
+  }
+  return rc;
+}
+
+/*
+** Evaluate an expression.  Return the result as a JSON object.
+** Return TRUE if and only if all of the following are true:
+**
+**   (1)  pA exists
+**   (2)  pB exists and is an array or structure.
+**   (3)  pA is value contained within pB
+*/
+static int withinOperator(JsonNode *pA, JsonNode *pB){
+  int rc = 0;
+  if( pA==0 ) return 0;
+  if( pB==0 ) return 0;
+  switch( pB->eJType ){
+    case XJD1_ARRAY: {
+      int i;
+      for(i=0; i<pB->u.ar.nElem; i++){
+        if( xjd1JsonCompare(pA, pB->u.ar.apElem[i], 0)==0 ){
+          rc = 1;
+          break;
+        }
+      }
+      break;
+    }
+    case XJD1_STRUCT: {
+      JsonStructElem *p;
+      for(p=pB->u.st.pFirst; p; p=p->pNext){
+        if( xjd1JsonCompare(pA, p->pValue, 0)==0 ){
+          rc = 1;
+          break;
+        }
+      }
+      break;
+    }
+    default: {
+      rc = 0;
+    }
+  }
+  return rc;
+}
+
+/*
+** Assuming zIn points to the first byte of a UTF-8 character,
+** advance zIn to point to the first byte of the next UTF-8 character.
+*/
+#define XJD1_SKIP_UTF8(zIn) {                          \
+    if( (*(zIn++))>=0xc0 ){                            \
+          while( (*zIn & 0xc0)==0x80 ){ zIn++; }       \
+    }                                                  \
+}
+
+
+/*
+** Evaluate an expression.  Return the result as a JSON object.
+**
+** The caller must free the returned JSON by a call xjdJsonFree().
+*/
+JsonNode *xjd1ExprEval(Expr *p){
+  JsonNode *pRes;
+  double rLeft, rRight;
+  int c;
+  JsonNode *pJLeft, *pJRight;
+
+  if( p==0 ) return nullJson();
+  switch( p->eType ){
+    case TK_JVALUE: {
+      return xjd1JsonRef(p->u.json.p);
+    }
+
+    case TK_DOT: {
+      JsonNode *pBase = xjd1ExprEval(p->u.lvalue.pLeft);
+      pRes = getProperty(pBase, p->u.lvalue.zId);
+      xjd1JsonFree(pBase);
+      return pRes;
+    }
+
+    /* The x[y] operator. The result depends on the type of value x.
+    **
+    ** If x is of type XJD1_STRUCT, then expression y is converted to
+    ** a string. The value returned is the value of property y of
+    ** object x.
+    **
+    ** If x is of type XJD1_ARRAY, then expression y is converted to
+    ** a number. If that number is an integer, then it is the index of
+    ** the array element to return.
+    **
+    ** If x is of type XJD1_STRING, then it is treated as an array of
+    ** characters. Processing proceeds as for XJD1_ARRAY.
+    */
+    case TK_LB: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      pRes = 0;
+
+      switch( pJLeft->eJType ){
+        case XJD1_STRUCT: {
+          String idx;
+          xjd1StringInit(&idx, 0, 0);
+          xjd1JsonToString(pJRight, &idx);
+          pRes = getProperty(pJLeft, idx.zBuf);
+          xjd1StringClear(&idx);
+          break;
+        }
+
+        case XJD1_ARRAY: {
+          int iIdx;
+          if( xjd1JsonToReal(pJRight, &rRight) ) break;
+          iIdx = (int)rRight;
+          if( (double)iIdx==rRight && iIdx>=0 && iIdx<pJLeft->u.ar.nElem ){
+            pRes = xjd1JsonRef(pJLeft->u.ar.apElem[iIdx]);
+          }
+          break;
+        }
+
+        case XJD1_STRING: {
+          int iIdx;
+          if( xjd1JsonToReal(pJRight, &rRight) ) break;
+          iIdx = (int)rRight;
+          if( (double)iIdx==rRight && iIdx>=0 ){
+            unsigned char *z = (unsigned char*)pJLeft->u.z;
+            for(; *z && iIdx!=0; iIdx--){
+              XJD1_SKIP_UTF8(z);
+            }
+            if( *z ){
+              String x;
+              unsigned char *zEnd = (unsigned char*)z;
+              pRes = xjd1JsonNew(0);
+              if( pRes ){
+                XJD1_SKIP_UTF8(zEnd);
+                xjd1StringInit(&x, 0, 0);
+                xjd1StringAppend(&x, (char*)z, zEnd-(unsigned char*)z);
+                pRes->eJType = XJD1_STRING;
+                pRes->u.z = xjd1StringGet(&x);
+              }
+            }
+          }
+          break;
+        }
+
+        default:
+          break;
+      }
+
+      xjd1JsonFree(pJLeft);
+      xjd1JsonFree(pJRight);
+      if( pRes==0 ) pRes = nullJson();
+      return pRes;
+    }
+
+    case TK_ID: {
+      if( p->u.id.pQuery ){
+        assert( p->pStmt->pCmd->eCmdType==TK_SELECT );
+        return xjd1QueryDoc(p->u.id.pQuery, p->u.id.iDatasrc);
+      }else{
+        assert( p->pStmt->pCmd->eCmdType==TK_DELETE
+             || p->pStmt->pCmd->eCmdType==TK_UPDATE
+        );
+        return xjd1StmtDoc(p->pStmt);
+      }
+    }
+
+    /* The following two logical operators work in the same way as their
+    ** javascript counterparts. i.e.
+    **
+    **    1. "x AND y" is equivalent to "x ? y : x"
+    **    2. "x OR y" is equivalent to "x ? x : y"
+    */
+    case TK_AND:
+    case TK_OR: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      if( isTrue(pJLeft)==(p->eType==TK_OR) ){
+        pRes = pJLeft;
+      }else{
+        xjd1JsonFree(pJLeft);
+        pRes = xjd1ExprEval(p->u.bi.pRight);
+      }
+      return pRes;
+    }
+
+    case TK_FUNCTION: {
+      pRes = xjd1FunctionEval(p);
+      return pRes;
+    }
+
+    /* A scalar sub-query. The result of this is the first object
+    ** returned by executing the query. Or, if the query returns zero
+    ** rows, a NULL value.
+    **
+    ** TODO: Handle correlated and uncorrelated sub-queries differently.
+    */
+    case TK_SELECT: {
+      Query *pQuery = p->u.subq.p;
+      int rc;
+      rc = xjd1QueryStep(pQuery);
+      if( rc==XJD1_ROW ){
+        pRes = xjd1QueryDoc(pQuery, 0);
+      }else if( rc==XJD1_DONE ){
+        pRes = xjd1JsonNew(0);
+        if( pRes ) pRes->eJType = XJD1_NULL;
+      }
+      xjd1QueryRewind(pQuery);
+      return pRes;
+    }
+  }
+
+
+  pRes = xjd1JsonNew(0);
+  if( pRes==0 ) return 0;
+  pRes->eJType = XJD1_NULL;
+  switch( p->eType ){
+    case TK_STRUCT: {
+      int i;
+      JsonStructElem *pElem, **ppPrev;
+      ExprList *pList = p->u.st;
+      ppPrev = &pRes->u.st.pFirst;
+      pRes->eJType = XJD1_STRUCT;
+      for(i=0; i<pList->nEItem; i++){
+        ExprItem *pItem = &pList->apEItem[i];
+        pElem = xjd1_malloc( sizeof(*pElem) );
+        if( pElem==0 ) break;
+        *ppPrev = pRes->u.st.pLast = pElem;
+        ppPrev = &pElem->pNext;
+        memset(pElem, 0, sizeof(*pElem));
+        pElem->zLabel = xjd1PoolDup(0, pItem->zAs, -1);
+        pElem->pValue = xjd1ExprEval(pItem->pExpr);
+      }
+      break;
+    }
+    case TK_ARRAY: {
+      int i;
+      ExprList *pList = p->u.st;
+      pRes->u.ar.apElem = xjd1_malloc( pList->nEItem*sizeof(JsonNode*) );
+      if( pRes->u.ar.apElem ){
+        pRes->u.ar.nElem = pList->nEItem;
+        pRes->eJType = XJD1_ARRAY;
+        for(i=0; i<pList->nEItem; i++){
+          ExprItem *pItem = &pList->apEItem[i];
+          pRes->u.ar.apElem[i] = xjd1ExprEval(pItem->pExpr);
+        }
+      }
+      break;
+    }
+    case TK_LIKEOP:
+    case TK_ILIKEOP:
+    case TK_EQEQ:
+    case TK_NE:
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      c = xjd1JsonCompare(pJLeft, pJRight, (p->eType == TK_ILIKEOP));
+      xjd1JsonFree(pJLeft);
+      xjd1JsonFree(pJRight);
+      switch( p->eType ){
+	case TK_ILIKEOP:
+	case TK_LIKEOP:
+        case TK_EQEQ: c = c==0;   break;
+        case TK_NE:   c = c!=0;   break;
+        case TK_LT:   c = c<0;    break;
+        case TK_LE:   c = c<=0;   break;
+        case TK_GT:   c = c>0;    break;
+        case TK_GE:   c = c>=0;   break;
+      }
+      pRes->eJType = c ? XJD1_TRUE : XJD1_FALSE;
+      break;
+    }
+    case TK_PLUS: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      if( isStr(pJLeft) || isStr(pJRight) ){
+        String x;
+        xjd1StringInit(&x, 0, 0);
+        xjd1JsonToString(pJLeft, &x);
+        xjd1JsonToString(pJRight, &x);
+        pRes->eJType = XJD1_STRING;
+        pRes->u.z = xjd1StringGet(&x);
+      }else{
+        xjd1JsonToReal(pJLeft, &rLeft);
+        xjd1JsonToReal(pJRight, &rRight);
+        pRes->u.r = rLeft+rRight;
+        pRes->eJType = XJD1_REAL;
+      }
+      xjd1JsonFree(pJLeft);
+      xjd1JsonFree(pJRight);
+      break;
+    }
+
+
+    case TK_STAR:
+    case TK_SLASH:
+    case TK_MINUS: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      xjd1JsonToReal(pJLeft, &rLeft);
+      if( p->u.bi.pRight==0 ){
+        assert( p->eType==TK_MINUS );
+        pRes->u.r = -1.0 * rLeft;
+      }else{
+        pJRight = xjd1ExprEval(p->u.bi.pRight);
+        xjd1JsonToReal(pJRight, &rRight);
+
+        switch( p->eType ){
+          case TK_MINUS:
+            pRes->u.r = rLeft-rRight;
+            break;
+
+          case TK_SLASH:
+            if( rRight!=0.0 ){
+              pRes->u.r = rLeft / rRight;
+            }
+            break;
+
+          case TK_STAR:
+            pRes->u.r = rLeft * rRight;
+            break;
+        }
+        xjd1JsonFree(pJRight);
+      }
+      pRes->eJType = XJD1_REAL;
+      xjd1JsonFree(pJLeft);
+      break;
+    }
+
+    case TK_BANG: {
+      if( xjd1ExprTrue(p->u.bi.pLeft) ){
+        pRes->eJType = XJD1_FALSE;
+      }else{
+        pRes->eJType = XJD1_TRUE;
+      }
+      break;
+    }
+
+    /* Bitwise operators: &, |, <<, >> and ~.
+    **
+    ** These follow the javascript conventions. Arguments are converted to
+    ** 64-bit real numbers, and then to 32-bit signed integers. The bitwise
+    ** operation is performed and the result converted back to a 64-bit
+    ** real number.
+    **
+    ** TBD: When XJD1 is enhance to feature an arbitrary precision integer
+    ** type, these will have to change somehow.
+    **
+    ** This block also contains the implementation of the modulo operator.
+    ** As it requires the same 32-bit integer conversions as the bitwise
+    ** operators.
+    */
+    case TK_REM:
+    case TK_RSHIFT:
+    case TK_LSHIFT:
+    case TK_BITAND:
+    case TK_BITOR: {
+      int iLeft;
+      int iRight;
+
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      xjd1JsonToReal(pJLeft, &rLeft);
+      xjd1JsonToReal(pJRight, &rRight);
+      iLeft = rLeft;
+      iRight = rRight;
+
+      pRes->eJType = XJD1_REAL;
+      switch( p->eType ){
+        case TK_RSHIFT:
+          if( iRight>=32 ){
+            pRes->u.r = (double)(iLeft<0 ? -1 : 0);
+          }else{
+            pRes->u.r = (double)(iLeft >> iRight);
+          }
+          break;
+
+        case TK_LSHIFT:
+          if( iRight>=32 ){
+            pRes->u.r = 0;
+          }else{
+            pRes->u.r = (double)(iLeft << iRight);
+          }
+          break;
+
+        case TK_BITAND: pRes->u.r = (double)(iLeft & iRight); break;
+        case TK_BITOR:  pRes->u.r = (double)(iLeft | iRight); break;
+        case TK_REM:    pRes->u.r = (double)(iLeft % iRight); break;
+      }
+      break;
+    }
+
+    case TK_BITNOT: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      xjd1JsonToReal(pJLeft, &rLeft);
+      pRes->eJType = XJD1_REAL;
+      pRes->u.r = (double)(~((int)rLeft));
+      break;
+    }
+
+    case TK_QM: {
+      if( xjd1ExprTrue(p->u.tri.pTest) ){
+        pRes = xjd1ExprEval(p->u.tri.pIfTrue);
+      }else{
+        pRes = xjd1ExprEval(p->u.tri.pIfFalse);
+      }
+      break;
+    }
+
+    case TK_IN: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      pRes->eJType = inOperator(pJLeft, pJRight) ? XJD1_TRUE : XJD1_FALSE;
+      xjd1JsonFree(pJLeft);
+      xjd1JsonFree(pJRight);
+      break;
+    }
+
+    case TK_WITHIN: {
+      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
+      pJRight = xjd1ExprEval(p->u.bi.pRight);
+      pRes->eJType = withinOperator(pJLeft, pJRight) ? XJD1_TRUE : XJD1_FALSE;
+      xjd1JsonFree(pJLeft);
+      xjd1JsonFree(pJRight);
+      break;
+    }
+
+    default: {
+      pRes->eJType = XJD1_NULL;
+      break;
+    }
+  }
+  return pRes;
+}
+
+/*
+** Return non-zero if the evaluation of the given expression should be
+** considered TRUE in a boolean context. For example in result of a
+** WHERE or HAVING clause.
+*/
+int xjd1ExprTrue(Expr *p){
+  int rc = 0;
+  JsonNode *pValue = xjd1ExprEval(p);
+  if( pValue ){
+    rc = isTrue(pValue);
+    assert( rc==1 || rc==0 );
+    xjd1JsonFree(pValue);
+  }
+  return rc;
+}
+

+ 489 - 0
unql/src/func.c

@@ -0,0 +1,489 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+*/
+#include "xjd1Int.h"
+
+/*
+** A structure used to store the definition of a query language function
+** or aggregate.
+*/
+struct Function {
+  int nMinArg;
+  int nMaxArg;
+  const char *zName;
+  JsonNode *(*xFunc)(int nArg, JsonNode **apArg);
+  int (*xStep)(int nArg, JsonNode **apArg, void **, int *pbSave);
+  JsonNode *(*xFinal)(void *);
+};
+
+/*************************************************************************
+** Start of implementation of aggregate functions:
+**
+**   count()
+**     This aggregate may be invoked with zero or one argument. If no
+**     arguments are specified, it returns the number of rows visited. If
+**     one argument is supplied, it returns the number of rows visited
+**     for which the argument was anything other than a NULL. In all cases,
+**     this function returns a non-negative integer value.
+**
+**   min()
+**     This aggregate is invoked with exactly one argument. If no rows are
+**     visited, NULL is returned. Otherwise, it returns a copy of the
+**     smallest argument it is passed. The comparisons used to determine
+**     which argument is the smallest are performed using the same rules
+**     as for the < and > operators.
+**
+**   max()
+**     This aggregate is invoked with exactly one argument. If no rows are
+**     visited, NULL is returned. Otherwise, it returns a copy of the
+**     largest argument it is passed. The comparisons used to determine
+**     which argument is the largest are performed using the same rules
+**     as for the < and > operators.
+**
+**   array()
+**     This aggregate is invoked with exactly one argument. If no rows are
+**     visited, an empty array is returned. Otherwise, an array containing
+**     the argument passed to this aggregate for each row visited is returned.
+**     The order in which the values appear in the returned array is not
+**     defined.
+**
+**   sum()
+**     This aggregate is invoked with exactly one argument. If no rows are
+**     visited, the value 0.0 (a number) is returned. Otherwise, each argument
+**     is converted to a number and the sum of all converted arguments
+**     returned. Any value that cannot be converted to a number (i.e. a struct,
+**     an array or any string value that does not look like a number) is
+**     treated as 0.0.
+**
+**   avg()
+**     This aggregate is invoked with exactly one argument. If no rows are
+**     visited, NULL is returned. Otherwise, the expression avg(X) is
+**     equivalent to (sum(X)/count()).
+*/
+
+/*
+** Aggregate function count().
+*/
+static int xCountStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  int *pnCount = (int *)*pp;
+  if( pnCount==0 ){
+    pnCount = xjd1MallocZero(sizeof(int));
+    *pp = (void *)pnCount;
+  }
+  if( nArg==0 || apArg[0]->eJType!=XJD1_NULL ){
+    (*pnCount)++;
+  }
+  return XJD1_OK;
+}
+static JsonNode *xCountFinal(void *p){
+  JsonNode *pRet;
+  pRet = xjd1JsonNew(0);
+  pRet->eJType = XJD1_REAL;
+  if( p ){
+    pRet->u.r = (double)*(int *)p;
+    xjd1_free(p);
+  }
+  return pRet;
+}
+
+/*
+** Aggregate functions min() and max().
+*/
+static int xMinStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  JsonNode *pArg;
+  JsonNode *pBest = *pp;
+  assert( nArg==1 );
+  pArg = apArg[0];
+  if( !pBest || xjd1JsonCompare(pArg, pBest, 0)<0 ){
+    xjd1JsonFree(pBest);
+    *pp = (void *)xjd1JsonRef(pArg);
+    *pbSave = 1;
+  }
+  return XJD1_OK;
+}
+static int xMaxStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  JsonNode *pArg;
+  JsonNode *pBest = *pp;
+  assert( nArg==1 );
+  pArg = apArg[0];
+  if( !pBest || xjd1JsonCompare(pArg, pBest, 0)>0 ){
+    xjd1JsonFree(pBest);
+    *pp = (void *)xjd1JsonRef(pArg);
+    *pbSave = 1;
+  }
+  return XJD1_OK;
+}
+static JsonNode *xMinMaxFinal(void *p){
+  JsonNode *pRet;
+  if( p ){
+    pRet = (JsonNode *)p;
+  }else{
+    pRet = xjd1JsonNew(0);
+    pRet->eJType = XJD1_NULL;
+  }
+  return pRet;
+}
+
+/*
+** Aggregate function array().
+*/
+static int xArrayStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  JsonNode *pArray = (JsonNode *)*pp;
+  JsonNode *pArg = apArg[0];
+  int nNew;
+  if( !pArray ){
+    pArray = xjd1JsonNew(0);
+    pArray->eJType = XJD1_ARRAY;
+    *pp = (void *)pArray;
+  }
+  nNew = pArray->u.ar.nElem+1;
+  pArray->u.ar.apElem = (JsonNode **)xjd1_realloc(
+      pArray->u.ar.apElem, nNew*sizeof(JsonNode *)
+  );
+  pArray->u.ar.nElem = nNew;
+  pArray->u.ar.apElem[nNew-1] = xjd1JsonRef(pArg);
+  return XJD1_OK;
+}
+static JsonNode *xArrayFinal(void *p){
+  JsonNode *pArray = (JsonNode *)p;
+  if( !pArray ){
+    pArray = xjd1JsonNew(0);
+    pArray->eJType = XJD1_ARRAY;
+  }
+  return pArray;
+}
+
+/*
+** Aggregate function sum()
+*/
+static int xSumStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  JsonNode *pVal = (JsonNode *)*pp;
+  double rVal = 0.0;
+  if( !pVal ){
+    pVal = xjd1JsonNew(0);
+    pVal->eJType = XJD1_REAL;
+    *pp = (void *)pVal;
+  }
+  if( XJD1_OK==xjd1JsonToReal(apArg[0], &rVal) ){
+    pVal->u.r += rVal;
+  }
+  return XJD1_OK;
+}
+static JsonNode *xSumFinal(void *p){
+  JsonNode *pVal = (JsonNode *)p;
+  if( !pVal ){
+    pVal = xjd1JsonNew(0);
+    pVal->eJType = XJD1_REAL;
+  }
+  return pVal;
+}
+
+/*
+** Aggregate function avg()
+*/
+typedef struct AvgCtx AvgCtx;
+struct AvgCtx {
+  int nRow;
+  double rSum;
+};
+static int xAvgStep(int nArg, JsonNode **apArg, void **pp, int *pbSave){
+  AvgCtx *pCtx = (AvgCtx *)*pp;
+  double rVal = 0.0;
+  if( !pCtx ){
+    pCtx = xjd1MallocZero(sizeof(AvgCtx));
+    *pp = (void *)pCtx;
+  }
+  pCtx->nRow++;
+  if( XJD1_OK==xjd1JsonToReal(apArg[0], &rVal) ){
+    pCtx->rSum += rVal;
+  }
+  return XJD1_OK;
+}
+static JsonNode *xAvgFinal(void *p){
+  AvgCtx *pCtx = (AvgCtx *)p;
+  JsonNode *pRet;
+
+  pRet = xjd1JsonNew(0);
+  if( pCtx ){
+    pRet->eJType = XJD1_REAL;
+    pRet->u.r = pCtx->rSum/(double)pCtx->nRow;
+    xjd1_free(pCtx);
+  }else{
+    pRet->eJType = XJD1_NULL;
+  }
+
+  return pRet;
+}
+
+/*
+** End of implementation of aggregates.
+*************************************************************************/
+
+/*************************************************************************
+** Start of implementation of scalar functions:
+**
+**   length()
+**     This function is called with exactly one argument. It converts
+**     that argument to a string, and returns the number of characters
+**     in that string.
+**
+*/
+
+/*
+** Scalar function "length(x)".
+*/
+static JsonNode *xLength(int nArg, JsonNode **apArg){
+  JsonNode *pRet;
+  JsonNode *pStr;
+  int nRet;
+  assert( nArg==1 );
+
+  pStr = apArg[0];
+  if( pStr->eJType==XJD1_STRING ){
+    nRet = xjd1Strlen30(pStr->u.z);
+  }else{
+    String str;
+    xjd1StringInit(&str, 0, 0);
+    xjd1JsonToString(pStr, &str);
+    nRet = str.nUsed;
+    xjd1StringClear(&str);
+  }
+
+  pRet = xjd1JsonNew(0);
+  pRet->eJType = XJD1_REAL;
+  pRet->u.r = (double)nRet;
+
+  return pRet;
+}
+
+/*
+** End of implementation of scalar functions.
+*************************************************************************/
+
+int xjd1AggregateInit(xjd1_stmt *pStmt, Query *pQuery, Expr *p){
+  Aggregate *pAgg;
+
+  assert( pQuery->eQType==TK_SELECT );
+  pAgg = pQuery->u.simple.pAgg;
+  if( pAgg==0 ){
+    pAgg = (Aggregate *)xjd1PoolMallocZero(&pStmt->sPool, sizeof(Aggregate));
+    if( pAgg==0 ) return XJD1_NOMEM;
+    pQuery->u.simple.pAgg = pAgg;
+  }
+
+  if( p ){
+    static const int ARRAY_ALLOC_INCR = 8;
+
+    if( (pAgg->nExpr % ARRAY_ALLOC_INCR)==0 ){
+      int nByte;                    /* Size of new allocation in bytes */
+      int nCopy;                    /* Size of old allocation in bytes */
+      AggExpr *aNew;                /* Pointer to new allocation */
+
+      nByte = (pAgg->nExpr + ARRAY_ALLOC_INCR) * sizeof(AggExpr);
+      nCopy = pAgg->nExpr * sizeof(AggExpr);
+      aNew = (AggExpr *)xjd1PoolMallocZero(&pStmt->sPool, nByte);
+
+      if( aNew==0 ) return XJD1_NOMEM;
+      memcpy(aNew, pAgg->aAggExpr, nCopy);
+      pAgg->aAggExpr = aNew;
+    }
+
+    p->u.func.iAgg = pAgg->nExpr;
+    pAgg->aAggExpr[pAgg->nExpr++].pExpr = p;
+  }
+
+  return XJD1_OK;
+}
+
+/*
+** The expression passed as the first argument is of type TK_FUNCTION.
+** This function initializes the expression object. If successful, XJD1_OK
+** is returned. Otherwise, an error code is returned and an error left in
+** the pStmt statement handle.
+*/
+int xjd1FunctionInit(Expr *p, xjd1_stmt *pStmt, Query *pQuery, int eExpr){
+  char *zName;
+  int nArg;
+  int nByte;
+  int i;
+  int bAggOk;
+  int bWrongNumArgs = 0;
+
+  static Function aFunc[] = {
+    {  1,1, "length", xLength, 0, 0 },
+
+    {  0,1, "count", 0, xCountStep, xCountFinal  },
+    {  1,1, "min",   0, xMinStep,   xMinMaxFinal },
+    {  1,1, "max",   0, xMaxStep,   xMinMaxFinal },
+    {  1,1, "array", 0, xArrayStep, xArrayFinal  },
+    {  1,1, "sum",   0, xSumStep,   xSumFinal    },
+    {  1,1, "avg",   0, xAvgStep,   xAvgFinal    },
+  };
+
+  assert( p->eType==TK_FUNCTION && p->eClass==XJD1_EXPR_FUNC );
+  assert( pQuery || eExpr==0 );
+
+  /* Set bAggOk to true if aggregate functions may be used in this context. */
+  bAggOk = (pQuery && pQuery->eQType==TK_SELECT
+        && (eExpr==XJD1_EXPR_RESULT || eExpr==XJD1_EXPR_GROUPBY
+         || eExpr==XJD1_EXPR_HAVING || eExpr==XJD1_EXPR_ORDERBY
+  ));
+
+  zName = p->u.func.zFName;
+  nArg = p->u.func.args->nEItem;
+
+  for(i=0; i<ArraySize(aFunc); i++){
+    Function *pFunc = &aFunc[i];
+    assert( (pFunc->xFunc==0)==(pFunc->xStep && pFunc->xFinal) );
+    if( !strcmp(pFunc->zName, zName) ){
+      if( nArg<=pFunc->nMaxArg && nArg>=pFunc->nMinArg ){
+        if( pFunc->xStep ){
+          int rc;
+          if( bAggOk==0 ){
+            const char *zErrMsg = "illegal use of aggregate function: %s";
+            xjd1StmtError(pStmt, XJD1_ERROR, zErrMsg, zName);
+            return XJD1_ERROR;
+          }
+
+          /* Add this aggregate function to the Query.pAgg->apExpr[] array. */
+          rc = xjd1AggregateInit(pStmt, pQuery, p);
+          if( rc!=XJD1_OK ) return rc;
+        }
+        p->u.func.pFunction = pFunc;
+      }else{
+        bWrongNumArgs = 1;
+      }
+      break;
+    }
+  }
+
+  if( !p->u.func.pFunction ){
+    if( bWrongNumArgs ){
+      xjd1StmtError(pStmt, XJD1_ERROR,
+          "wrong number of arguments to function %s()", zName);
+    }else{
+      xjd1StmtError(pStmt, XJD1_ERROR, "no such function: %s", zName);
+    }
+    return XJD1_ERROR;
+  }
+
+  nByte = sizeof(JsonNode *) * nArg;
+  p->u.func.apArg = (JsonNode **)xjd1PoolMallocZero(&pStmt->sPool, nByte);
+  if( !p->u.func.apArg ){
+    return XJD1_NOMEM;
+  }
+
+  return XJD1_OK;
+}
+
+static int aggExprStep(AggExpr *pAggExpr, int *pbSave){
+  Expr *p = pAggExpr->pExpr;
+  Function *pFunc = p->u.func.pFunction;
+  int i;
+  int nItem = p->u.func.args->nEItem;
+
+  assert( p->eType==TK_FUNCTION && p->eClass==XJD1_EXPR_FUNC );
+  assert( pFunc->xStep && pFunc->xFinal && pFunc->xFunc==0 );
+
+  for(i=0; i<nItem; i++){
+    p->u.func.apArg[i] = xjd1ExprEval(p->u.func.args->apEItem[i].pExpr);
+  }
+
+  pFunc->xStep(nItem, p->u.func.apArg, &pAggExpr->pAggCtx, pbSave);
+  for(i=0; i<nItem; i++){
+    xjd1JsonFree(p->u.func.apArg[i]);
+  }
+
+  return XJD1_OK;
+}
+
+int xjd1AggregateStep(
+  Aggregate *pAgg,
+  int *pbSave                     /* OUT: True if this row should be saved */
+){
+  int i;                /* Used to iterate through aggregate functions */
+  for(i=0; i<pAgg->nExpr; i++){
+    int rc = aggExprStep(&pAgg->aAggExpr[i], pbSave);
+    if( rc!=XJD1_OK ) return rc;
+  }
+  return XJD1_OK;;
+}
+
+int xjd1AggregateFinalize(Aggregate *pAgg){
+  int i;
+  for(i=0; i<pAgg->nExpr; i++){
+    AggExpr *pAggExpr = &pAgg->aAggExpr[i];
+    Expr *p = pAggExpr->pExpr;
+
+    assert( i==p->u.func.iAgg );
+    xjd1JsonFree(pAggExpr->pValue);
+    pAggExpr->pValue = p->u.func.pFunction->xFinal(pAggExpr->pAggCtx);
+    pAggExpr->pAggCtx = 0;
+  }
+
+  return XJD1_OK;
+}
+
+/*
+** Call any outstanding xFinal() functions for aggregate functions in the
+** query. This is required to reset the aggregate contexts when a query is
+** rewound following an error.
+*/
+void xjd1AggregateClear(Query *pQuery){
+  Aggregate *pAgg = pQuery->u.simple.pAgg;
+
+  assert( pQuery->eQType==TK_SELECT );
+  if( pAgg ){
+    int i;
+    for(i=0; i<pAgg->nExpr; i++){
+      AggExpr *pAggExpr = &pAgg->aAggExpr[i];
+      Expr *p = pAggExpr->pExpr;
+
+      xjd1JsonFree( p->u.func.pFunction->xFinal(pAggExpr->pAggCtx) );
+      xjd1JsonFree( pAggExpr->pValue );
+      pAggExpr->pAggCtx = 0;
+      pAggExpr->pValue = 0;
+    }
+  }
+}
+
+JsonNode *xjd1FunctionEval(Expr *p){
+  JsonNode *pRet;
+  Function *pFunc = p->u.func.pFunction;
+
+  assert( p->eType==TK_FUNCTION && p->eClass==XJD1_EXPR_FUNC );
+
+  if( pFunc->xFunc ){
+    /* A scalar function. */
+    int i;
+    int nItem = p->u.func.args->nEItem;
+    for(i=0; i<nItem; i++){
+      p->u.func.apArg[i] = xjd1ExprEval(p->u.func.args->apEItem[i].pExpr);
+    }
+
+    pRet = pFunc->xFunc(nItem, p->u.func.apArg);
+    for(i=0; i<nItem; i++){
+      xjd1JsonFree(p->u.func.apArg[i]);
+    }
+  }else{
+    AggExpr *pAggExpr = &p->pQuery->u.simple.pAgg->aAggExpr[p->u.func.iAgg];
+    pRet = xjd1JsonRef(pAggExpr->pValue);
+  }
+
+  return pRet;
+}
+

+ 868 - 0
unql/src/json.c

@@ -0,0 +1,868 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code used to parse and render JSON strings.
+*/
+#include "xjd1Int.h"
+#include <ctype.h>
+
+
+/*
+** Change a JsonNode to be a NULL.  Any substructure is deleted.
+*/
+void xjd1JsonToNull(JsonNode *p){
+  if( p==0 ) return;
+  switch( p->eJType ){
+    case XJD1_STRING: {
+      xjd1_free(p->u.z);
+      break;
+    }
+    case XJD1_ARRAY: {
+      int i;
+      for(i=0; i<p->u.ar.nElem; i++){
+        xjd1JsonFree(p->u.ar.apElem[i]);
+      }
+      xjd1_free(p->u.ar.apElem);
+      break;
+    }
+    case XJD1_STRUCT: {
+      JsonStructElem *pElem, *pNext;
+      for(pElem=p->u.st.pFirst; pElem; pElem=pNext){
+        pNext = pElem->pNext;
+        xjd1_free(pElem->zLabel);
+        xjd1JsonFree(pElem->pValue);
+        xjd1_free(pElem);
+      }
+      break;
+    }
+  }
+  p->eJType = XJD1_NULL;
+}
+
+/*
+** Reclaim memory used by JsonNode objects
+*/
+void xjd1JsonFree(JsonNode *p){
+  if( p && (--p->nRef)<=0 ){
+    xjd1JsonToNull(p);
+    xjd1_free(p);
+  }
+}
+
+/*
+** Allocate a new Json node.
+*/
+JsonNode *xjd1JsonNew(Pool *pPool){
+  JsonNode *p;
+  if( pPool ){
+    p = xjd1PoolMalloc(pPool, sizeof(*p));
+    if( p ) p->nRef = 10000;
+  }else{
+    p = xjd1_malloc( sizeof(*p) );
+    if( p ){
+      memset(p, 0, sizeof(*p));
+      p->nRef = 1;
+    }
+  }
+  return p;
+}
+
+/*
+** Increase the reference count on a JSON object.
+**
+** The object is freed when its reference count reaches zero.
+*/
+JsonNode *xjd1JsonRef(JsonNode *p){
+  if( p ){
+    p->nRef++;
+  }
+  return p;
+}
+
+
+/*
+** Return a deep copy of a JSON object.
+*/
+JsonNode *xjd1JsonDeepCopy(JsonNode *p){
+  JsonNode *pNew;
+  if( p==0 ) return 0;
+  pNew = xjd1JsonNew(0);
+  if( pNew==0 ) return 0;
+  pNew->eJType = p->eJType;
+  pNew->u = p->u;
+  switch( pNew->eJType ){
+    case XJD1_STRING: {
+      pNew->u.z = xjd1PoolDup(0, p->u.z, -1);
+      break;
+    }
+    case XJD1_ARRAY: {
+      JsonNode **ap;
+      pNew->u.ar.apElem = ap = xjd1_malloc( sizeof(JsonNode*)*p->u.ar.nElem );
+      if( ap==0 ){
+        pNew->eJType = XJD1_NULL;
+      }else{
+        int i;
+        pNew->u.ar.nElem = p->u.ar.nElem;
+        for(i=0; i<p->u.ar.nElem; i++){
+          ap[i] = xjd1JsonDeepCopy(p->u.ar.apElem[i]);
+        }
+      }
+      break;
+    }
+    case XJD1_STRUCT: {
+      JsonStructElem *pSrc, *pDest, **ppPrev;
+      ppPrev = &pNew->u.st.pFirst;
+      for(pSrc=p->u.st.pFirst; pSrc; pSrc=pSrc->pNext){
+        pNew->u.st.pLast = pDest = xjd1_malloc( sizeof(*pDest) );
+        if( pDest==0 ) break;
+        memset(pDest, 0, sizeof(*pDest));
+        *ppPrev = pDest;
+        ppPrev = &pDest->pNext;
+        pDest->zLabel = xjd1PoolDup(0, pSrc->zLabel, -1);
+        pDest->pValue = xjd1JsonDeepCopy(pSrc->pValue);
+      }
+      break;
+    }
+  }
+  return pNew;
+}
+
+/*
+** Return an editable JSON object.  A JSON object is editable if its
+** reference count is exactly 1.  If the input JSON object has a reference
+** count greater than 1, then make a copy and return the copy.
+*/
+JsonNode *xjd1JsonEdit(JsonNode *p){
+  if( p==0 ) return 0;
+  if( p->nRef==1 ) return p;
+  return xjd1JsonDeepCopy(p);
+}
+
+
+/* Render a string as a string literal.
+*/
+void renderString(String *pOut, const char *z){
+  int n, i, j, c;
+  char *zOut;
+  for(i=n=0; (c=z[i])!=0; i++, n++){
+    switch(c)
+    {
+    case '"':
+    case '\\':
+    case '\n':
+    case '\r':
+    case '\t':
+    case '\f':
+    case '\b':
+        ++n;
+        break;
+    }
+  }
+  xjd1StringAppend(pOut, 0, n+3);
+  zOut = xjd1StringText(pOut);
+  if( zOut ){
+    zOut += xjd1StringLen(pOut);
+    zOut[0] = '"';
+    for(i=0, j=1; (c=z[i])!=0; i++){
+      switch(c)
+      {
+        case '"':
+        case '\\':
+          zOut[j++] = '\\';
+          break;
+        case '\n':
+          zOut[j++] = '\\';
+          zOut[j++] = 'n';
+          continue;
+          break;
+        case '\r':
+          zOut[j++] = '\\';
+          zOut[j++] = 'r';
+          continue;
+          break;
+        case '\t':
+          zOut[j++] = '\\';
+          zOut[j++] = 't';
+          continue;
+          break;
+        case '\f':
+          zOut[j++] = '\\';
+          zOut[j++] = 'f';
+          continue;
+          break;
+        case '\b':
+          zOut[j++] = '\\';
+          zOut[j++] = 'b';
+          continue;
+          break;
+      }
+      zOut[j++] = c;
+    }
+    zOut[j++] = '"';
+    zOut[j] = 0;
+    pOut->nUsed += j;
+  }
+}
+
+
+/*
+** Append the text for a JSON string.
+*/
+void xjd1JsonRender(String *pOut, const JsonNode *p){
+  if( p==0 ){
+    xjd1StringAppend(pOut, "null", 4);
+  }else{
+    switch( p->eJType ){
+      case XJD1_FALSE: {
+        xjd1StringAppend(pOut, "false", 5);
+        break;
+      }
+      case XJD1_TRUE: {
+        xjd1StringAppend(pOut, "true", 4);
+        break;
+      }
+      case XJD1_NULL: {
+        xjd1StringAppend(pOut, "null", 4);
+        break;
+      }
+      case XJD1_REAL: {
+        xjd1StringAppendF(pOut, "%.17g", p->u.r);
+        break;
+      }
+      case XJD1_STRING: {
+        renderString(pOut, p->u.z);
+        break;
+      }
+      case XJD1_ARRAY: {
+        char cSep = '[';
+        int i;
+        if( p->u.ar.nElem==0 ) xjd1StringAppend(pOut, &cSep, 1);
+        for(i=0; i<p->u.ar.nElem; i++){
+          xjd1StringAppend(pOut, &cSep, 1);
+          cSep = ',';
+          xjd1JsonRender(pOut, p->u.ar.apElem[i]);
+        }
+        xjd1StringAppend(pOut, "]", 1);
+        break;
+      }
+      case XJD1_STRUCT: {
+        char cSep = '{';
+        JsonStructElem *pElem;
+        if( p->u.st.pFirst==0 ) xjd1StringAppend(pOut, &cSep, 1);
+        for(pElem=p->u.st.pFirst; pElem; pElem=pElem->pNext){
+          xjd1StringAppend(pOut, &cSep, 1);
+          cSep = ',';
+          renderString(pOut, pElem->zLabel);
+          xjd1StringAppend(pOut, ":", 1);
+          xjd1JsonRender(pOut, pElem->pValue);
+        }
+        xjd1StringAppend(pOut, "}", 1);
+        break;
+      }
+    }
+  }
+}
+
+/*
+** Attempt to convert a JSON object into a real number.  Return 0
+** if successful and 1 if there is an error.
+*/
+int xjd1JsonToReal(const JsonNode *p, double *pRes){
+  double x = 0.0;
+  *pRes = 0.0/x;
+  if( p==0 ) return 1;
+  switch( p->eJType ){
+    case XJD1_FALSE: {
+      *pRes = 0.0;
+      return 0;
+    }
+    case XJD1_TRUE: {
+      *pRes = 1.0;
+      return 0;
+    }
+    case XJD1_REAL: {
+      *pRes = p->u.r;
+      return 0;
+    }
+    case XJD1_STRING: {
+      char *zEnd;
+      if( isspace(p->u.z[0]) ){
+        return 1;
+      }else{
+        *pRes = strtod(p->u.z, &zEnd);
+        if( zEnd[0]!=0 ){
+          return 1;
+        }
+        return 0;
+      }
+    }
+  }
+  return 1;
+}
+
+/*
+** Attempt to convert a JSON object into a string.  Return 0
+** if successful and 1 if there is an error.
+*/
+int xjd1JsonToString(const JsonNode *p, String *pOut){
+  if( p==0 ){
+    xjd1StringAppend(pOut, "null", 4);
+    return 1;
+  }
+  switch( p->eJType ){
+    case XJD1_FALSE: {
+      xjd1StringAppend(pOut, "false", 5);
+      break;
+    }
+    case XJD1_TRUE: {
+      xjd1StringAppend(pOut, "true", 4);
+      break;
+    }
+    case XJD1_REAL: {
+      xjd1StringAppendF(pOut, "%.17g", p->u.r);
+      break;
+    }
+    case XJD1_NULL: {
+      xjd1StringAppend(pOut, "null", 4);
+      break;
+    }
+    case XJD1_STRING: {
+      xjd1StringAppend(pOut, p->u.z, -1);
+      break;
+    }
+    case XJD1_ARRAY: {
+      xjd1JsonRender(pOut, p);
+      break;
+    }
+    case XJD1_STRUCT: {
+      xjd1JsonRender(pOut, p);
+      break;
+    }
+  }
+  return 0;
+}
+
+/*
+** Compare to JSON objects.  Return negative, zero, or positive if the
+** first is less than, equal to, or greater than the second.
+*/
+int xjd1JsonCompare(const JsonNode *pLeft, const JsonNode *pRight, int insensitive){
+  if( pLeft==0 ){
+    return pRight ? -1 : 0;
+  }
+  if( pRight==0 ){
+    return 1;
+  }
+  if( pLeft->eJType!=pRight->eJType ){
+    return pLeft->eJType - pRight->eJType;
+  }
+  switch( pLeft->eJType ){
+    case XJD1_REAL: {
+      if( pLeft->u.r<pRight->u.r ){
+        return -1;
+      }
+      if( pLeft->u.r>pRight->u.r ){
+        return 1;
+      }
+      return 0;
+    }
+    case XJD1_STRING: {
+      if(insensitive) return strcasecmp(pLeft->u.z, pRight->u.z);
+      else return strcmp(pLeft->u.z, pRight->u.z);
+    }
+    case XJD1_ARRAY: {
+      int i, mx, c;
+      mx = pLeft->u.ar.nElem;
+      if( mx>pRight->u.ar.nElem ) mx = pRight->u.ar.nElem;
+      for(i=0; i<mx; i++){
+        c = xjd1JsonCompare(pLeft->u.ar.apElem[i], pRight->u.ar.apElem[i], 0);
+        if( c ) return c;
+      }
+      return pLeft->u.ar.nElem - pRight->u.ar.nElem;
+    }
+    case XJD1_STRUCT: {
+      JsonStructElem *pA = pLeft->u.st.pFirst;
+      JsonStructElem *pB = pRight->u.st.pFirst;
+      int c = 0;
+      while( pA && pB ){
+        c = strcmp(pA->zLabel, pB->zLabel);
+        if( c ) return c;
+        c = xjd1JsonCompare(pA->pValue, pB->pValue, 0);
+        if( c ) return c;
+        pA = pA->pNext;
+        pB = pB->pNext;
+      }
+      if( pA ){
+        return 1;
+      }
+      if( pB ){
+        return -1;
+      }
+      return 0;
+    }
+  }
+  return 0;
+}
+
+
+/* JSON parser token types */
+#define JSON_FALSE          XJD1_FALSE
+#define JSON_TRUE           XJD1_TRUE
+#define JSON_REAL           XJD1_REAL
+#define JSON_NULL           XJD1_NULL
+#define JSON_STRING         XJD1_STRING
+#define JSON_BEGIN_ARRAY    XJD1_ARRAY
+#define JSON_BEGIN_STRUCT   XJD1_STRUCT
+
+#define JSON_END_ARRAY     20
+#define JSON_COMMA         21
+#define JSON_END_STRUCT    22
+#define JSON_COLON         23
+#define JSON_EOF           24
+#define JSON_ERROR         25
+
+/* State of a JSON string tokenizer */
+typedef struct JsonStr JsonStr;
+struct JsonStr {
+  const char *zIn;        /* Complete input string */
+  int mxIn;               /* Number of characters in input string */
+  int iCur;               /* First character of current token */
+  int n;                  /* Number of charaters in current token */
+  int eType;              /* Type of current token */
+};
+
+/* Return the type of the current token */
+#define tokenType(X)  ((X)->eType)
+
+/* Return a pointer to the string of a token. */
+#define tokenString(X) (&(X)->zIn[(X)->iCur])
+
+/* Return TRUE if z[0..n-1] is the beginning of engineering notation:
+**
+**      [eE](+|-)[0-9]+
+*/
+static int isExp(const char *z, int n){
+  if( n<2 ) return 0;
+  if( z[0]!='e' && z[0]!='E' ) return 0;
+  if( xjd1Isdigit(z[1]) ) return 1;
+  if( n<3 || (z[1]!='-' && z[1]!='+') ) return 0;
+  if( xjd1Isdigit(z[2]) ) return 1;
+  return 0;
+}
+
+/* Advance to the next token */
+static void tokenNext(JsonStr *p){
+  int i, n;
+  const char *z = p->zIn;
+  int mx = p->mxIn;
+  char c;
+
+  i = p->n + p->iCur;
+  while( i<mx && xjd1Isspace(z[i]) ){ i++; }
+  if( i>=mx ) goto token_eof;
+  p->iCur = i;
+  switch( i<mx ? z[i] : 0 ){
+    case 0: {
+      goto token_eof;
+    }
+    case '"': {
+     for(n=1; i+n<mx && (c = z[i+n])!=0 && c!='"'; n++){
+        if( c=='\\' ) n++;
+      }
+      if( c=='"' ) n++;
+      if( i+n>mx ){ n = mx - i; c = 0; }
+      p->n = n;
+      p->eType = (c=='"' ? JSON_STRING : JSON_ERROR);
+      break;
+    }
+    case '{': {
+      p->n = 1;
+      p->eType = JSON_BEGIN_STRUCT;
+      break;
+    }
+    case '}': {
+      p->n = 1;
+      p->eType = JSON_END_STRUCT;
+      break;
+    }
+    case '[': {
+      p->n = 1;
+      p->eType = JSON_BEGIN_ARRAY;
+      break;
+    }
+    case ']': {
+      p->n = 1;
+      p->eType = JSON_END_ARRAY;
+      break;
+    }
+    case ',': {
+      p->n = 1;
+      p->eType = JSON_COMMA;
+      break;
+    }
+    case ':': {
+      p->n = 1;
+      p->eType = JSON_COLON;
+      break;
+    }
+    case 't': {
+      if( i+4<=mx && memcmp(&z[i],"true",4)==0
+       && (i+4==mx || xjd1Isident(z[i+4])==0)
+      ){
+        p->n = 4;
+        p->eType = JSON_TRUE;
+      }else{
+        p->n = 1;
+        p->eType = JSON_ERROR;
+      }
+      break;
+    }
+    case 'f': {
+      if( i+5<=mx && memcmp(&z[i],"false",5)==0
+       && (i+5==mx || xjd1Isident(z[i+5])==0)
+      ){
+        p->n = 5;
+        p->eType = JSON_FALSE;
+      }else{
+        p->n = 1;
+        p->eType = JSON_ERROR;
+      }
+      break;
+    }
+    case 'n': {
+      if( i+4<=mx && memcmp(&z[i],"null",4)==0
+       && (i+4==mx || xjd1Isident(z[i+4])==0)
+      ){
+        p->n = 4;
+        p->eType = JSON_NULL;
+      }else{
+        p->n = 1;
+        p->eType = JSON_ERROR;
+      }
+      break;
+    }
+    case '-':
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9': {
+      n = 0;
+      if( z[i]=='-' ){
+        n = 1;
+        if( i+1>=mx || !xjd1Isdigit(z[i+1]) ){
+          p->n = 1;
+          p->eType = JSON_ERROR;
+          break;
+        }
+      }
+      if( z[i+n]=='0' ){
+        n++;
+      }else{
+        while( i+n<mx && xjd1Isdigit(z[i+n]) ) n++;
+      }
+      if( i+n+1<mx && z[i+n]=='.' && xjd1Isdigit(z[i+n+1]) ){
+        n++;
+        while( i+n<mx && xjd1Isdigit(z[i+n]) ) n++;
+      }
+      if( isExp(&z[i+n], mx-(i+n)) ){
+        n += 2;
+        while( i+n<mx && xjd1Isdigit(z[i+n]) ) n++;
+      }
+      p->n = n;
+      p->eType = JSON_REAL;
+      break;
+    }
+    default: {
+      p->n = 1;
+      p->eType = JSON_ERROR;
+      break;
+    }
+  }
+  return;
+
+token_eof:
+  p->n = 0;
+  p->eType = JSON_EOF;
+  return;
+}
+
+/*
+** Dequote a string in-place.
+*/
+void xjd1DequoteString(char *z, int n){
+  int i, j;
+  char c;
+  assert( n>=2 );
+  assert( z[0]=='"' && z[n-1]=='"' );
+  for(i=1, j=0; i<n-1; i++){
+    if( z[i]!='\\' ){
+      z[j++] = z[i];
+    }else{
+      i++;
+      c = z[i];
+      if( c=='b' ){
+        z[j++] = '\b';
+      }else if( c=='f' ){
+        z[j++] = '\f';
+      }else if( c=='n' ){
+        z[j++] = '\n';
+      }else if( c=='r' ){
+        z[j++] = '\r';
+      }else if( c=='t' ){
+        z[j++] = '\t';
+      }else if( c=='u' && i<n-4 ){
+        /* FIX ME */
+      }else{
+        z[j++] = c;
+      }
+    }
+  }
+  z[j] = 0;
+}
+
+/* Convert the current token (which must be a string) into a true
+** string (resolving all of the backslash escapes) and return a pointer
+** to the true string.  Space is obtained from xjd1_malloc().
+*/
+static char *tokenDequoteString(JsonStr *pIn){
+  const char *zIn;
+  char *zOut;
+  //int n;
+  zIn = &pIn->zIn[pIn->iCur];
+  zOut = xjd1_malloc( pIn->n );
+  if( zOut==0 ) return 0;
+  assert( zIn[0]=='"' && zIn[pIn->n-1]=='"' );
+  //n = pIn->n-1;
+  memcpy(zOut, zIn, pIn->n);
+  xjd1DequoteString(zOut, pIn->n);
+  return zOut;
+}
+
+
+/* Enter point to the first token of the JSON object.
+** Exit pointing to the first token past end end of the
+** JSON object.
+*/
+static JsonNode *parseJson(JsonStr *pIn){
+  JsonNode *pNew;
+  pNew = xjd1JsonNew(0);
+  if( pNew==0 ) return 0;
+  pNew->eJType = tokenType(pIn);
+  switch( pNew->eJType ){
+    case JSON_BEGIN_STRUCT: {
+      JsonStructElem **ppTail;
+      tokenNext(pIn);
+      if( tokenType(pIn)==JSON_END_STRUCT ) break;
+      ppTail = &pNew->u.st.pFirst;
+      while( 1 ){
+        JsonStructElem *pElem;
+        if( tokenType(pIn)!=JSON_STRING ){
+          goto json_error;
+        }
+        pElem = xjd1_malloc( sizeof(*pElem) );
+        if( pElem==0 ) goto json_error;
+        memset(pElem, 0, sizeof(*pElem));
+        *ppTail = pElem;
+        pNew->u.st.pLast = pElem;
+        ppTail = &pElem->pNext;
+        pElem->zLabel = tokenDequoteString(pIn);
+        tokenNext(pIn);
+        if( tokenType(pIn)!=JSON_COLON ){
+          goto json_error;
+        }
+        tokenNext(pIn);
+        pElem->pValue = parseJson(pIn);
+        if( tokenType(pIn)==JSON_COMMA ){
+          tokenNext(pIn);
+        }else if( tokenType(pIn)==JSON_END_STRUCT ){
+          tokenNext(pIn);
+          break;
+        }else{
+          goto json_error;
+        }
+      }
+      break;
+    }
+    case JSON_BEGIN_ARRAY: {
+      int nAlloc = 0;
+      tokenNext(pIn);
+      if( tokenType(pIn)==JSON_END_ARRAY ){
+        tokenNext(pIn);
+        break;
+      }
+      while( 1 ){
+        if( pNew->u.ar.nElem>=nAlloc ){
+          JsonNode **pNewArray;
+          nAlloc = nAlloc*2 + 5;
+          pNewArray = xjd1_realloc(pNew->u.ar.apElem,
+                              sizeof(JsonNode*)*nAlloc);
+          if( pNewArray==0 ) goto json_error;
+          pNew->u.ar.apElem = pNewArray;
+        }
+        pNew->u.ar.apElem[pNew->u.ar.nElem++] = parseJson(pIn);
+        if( tokenType(pIn)==JSON_COMMA ){
+          tokenNext(pIn);
+        }else if( tokenType(pIn)==JSON_END_ARRAY ){
+          tokenNext(pIn);
+          break;
+        }else{
+          goto json_error;
+        }
+      }
+      break;
+    }
+    case JSON_STRING: {
+      pNew->u.z = tokenDequoteString(pIn);
+      tokenNext(pIn);
+      break;
+    }
+    case JSON_REAL: {
+      pNew->u.r = atof(tokenString(pIn));
+      tokenNext(pIn);
+      break;
+    }
+    case JSON_TRUE:
+    case JSON_FALSE:
+    case JSON_NULL: {
+      tokenNext(pIn);
+      break;
+    }
+    default: {
+      xjd1_free(pNew);
+      pNew = 0;
+      break;
+    }
+  }
+  return pNew;
+
+json_error:
+  xjd1JsonFree(pNew);
+  return 0;
+}
+
+/*
+** Parse up a JSON string
+*/
+JsonNode *xjd1JsonParse(const char *zIn, int mxIn){
+  JsonStr x;
+  x.zIn = zIn;
+  x.mxIn = mxIn>0 ? mxIn : xjd1Strlen30(zIn);
+  x.iCur = 0;
+  x.n = 0;
+  x.eType = 0;
+  tokenNext(&x);
+  return parseJson(&x);
+}
+
+/*
+** This function is used by the XJD1 shell in test mode. It assumes that
+** the string zIn contains a list of white-space separated JSON values.
+** It appends the contents of zIn to string pOut, making the following
+** edits:
+**
+**   1. All white-space that appears inside of struct or array values
+**      is removed. A single space characer is left between each top
+**      level value.
+**
+**   2. Double quotes are added to any unquoted sequences of alphabetic
+**      characters (which are illegal in JSON).
+**
+** For example, the following input:
+**
+**      {a:1,  "b":[1, 3, 4]}   [1,2]
+**
+** is transformed to:
+**
+**      {"a":1,"b":[1,3,4]} [1,2]
+**
+** before it is appended to pOut.
+*/
+int xjd1JsonTidy(String *pOut, const char *zIn){
+  int nOpen = 0;                  /* Number of open {} or [] brackets */
+  JsonStr x;                      /* Wrapper around zIn */
+
+  /* Set up the string wrapper to tokenize zIn */
+  x.zIn = zIn;
+  x.mxIn = xjd1Strlen30(zIn);
+  x.iCur = 0;
+  x.n = 0;
+  x.eType = 0;
+
+  while( 1 ){
+    int ePrev = x.eType;
+    tokenNext(&x);
+    if( x.eType==JSON_EOF ) break;
+    if( ePrev && nOpen==0 ) xjd1StringAppend(pOut, " ", 1);
+    if( x.eType==JSON_BEGIN_ARRAY || x.eType==JSON_BEGIN_STRUCT ) nOpen++;
+    if( x.eType==JSON_END_ARRAY   || x.eType==JSON_END_STRUCT ) nOpen--;
+    if( x.eType==JSON_ERROR && xjd1Isalpha(zIn[x.iCur]) ){
+      while( xjd1Isalpha(zIn[x.iCur+x.n]) ) x.n++;
+      xjd1StringAppend(pOut, "\"", 1);
+      xjd1StringAppend(pOut, &zIn[x.iCur], x.n);
+      xjd1StringAppend(pOut, "\"", 1);
+    }else{
+      xjd1StringAppend(pOut, &zIn[x.iCur], x.n);
+    }
+  }while( x.eType!=JSON_EOF );
+
+  return XJD1_OK;
+}
+
+/*
+** The JsonNode passed as the first argument must be of type XJD1_STRUCT.
+** This function adds a property to the object. The name of the new
+** property is passed as the second argument to this function. The third
+** argument is used as the initial value. If the property already exists,
+** it is replaced.
+**
+** This function decrements the reference count of the JsonNode passed
+** as the third argument. This means it is possible to do:
+**
+**     pVal = xjd1JsonNew();            // nRef==1
+**     xjd1JsonInsert(p, "x", pVal);    // Object p assumes ownership of pVal
+**
+** XJD1_OK is returned if the operation is completed successfully. Or
+** XJD1_NOMEM if an OOM error is encountered.
+*/
+int xjd1JsonInsert(JsonNode *p, const char *zLabel, JsonNode *pVal){
+  JsonStructElem *pElem;
+
+  assert( p && p->eJType==XJD1_STRUCT && p->nRef==1 );
+  for(pElem=p->u.st.pFirst; pElem; pElem=pElem->pNext){
+    if( strcmp(zLabel, pElem->zLabel)==0 ) break;
+  }
+  if( pElem ){
+    xjd1_free(pElem->zLabel);
+    xjd1JsonFree(pElem->pValue);
+  }else{
+    pElem = xjd1_malloc(sizeof(*pElem));
+    if( !pElem ){
+      xjd1JsonFree(pVal);
+      return XJD1_NOMEM;
+    }
+
+    if( p->u.st.pLast ){
+      p->u.st.pLast->pNext = pElem;
+    }else{
+      p->u.st.pFirst = pElem;
+    }
+    pElem->pNext = 0;
+    p->u.st.pLast = pElem;
+  }
+
+  pElem->pValue = pVal;
+  pElem->zLabel = xjd1PoolDup(0, zLabel, -1);
+  return (pElem->zLabel ? XJD1_OK : XJD1_NOMEM);
+}
+

+ 850 - 0
unql/src/lempar.c

@@ -0,0 +1,850 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
+#include <stdio.h>
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/* 
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands. 
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+**    YYCODETYPE         is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 terminals
+**                       and nonterminals.  "int" is used otherwise.
+**    YYNOCODE           is a number of type YYCODETYPE which corresponds
+**                       to no legal terminal or nonterminal number.  This
+**                       number is used to fill in empty slots of the hash 
+**                       table.
+**    YYFALLBACK         If defined, this indicates that one or more tokens
+**                       have fall-back values which should be used if the
+**                       original value of the token will not parse.
+**    YYACTIONTYPE       is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 rules and
+**                       states combined.  "int" is used otherwise.
+**    ParseTOKENTYPE     is the data type used for minor tokens given 
+**                       directly to the parser from the tokenizer.
+**    YYMINORTYPE        is the data type used for all minor tokens.
+**                       This is typically a union of many types, one of
+**                       which is ParseTOKENTYPE.  The entry in the union
+**                       for base tokens is called "yy0".
+**    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
+**                       zero the stack is dynamically sized using realloc()
+**    ParseARG_SDECL     A static variable declaration for the %extra_argument
+**    ParseARG_PDECL     A parameter declaration for the %extra_argument
+**    ParseARG_STORE     Code to store %extra_argument into yypParser
+**    ParseARG_FETCH     Code to extract %extra_argument from yypParser
+**    YYNSTATE           the combined number of states.
+**    YYNRULE            the number of rules in the grammar
+**    YYERRORSYMBOL      is the code number of the error symbol.  If not
+**                       defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
+
+/* The yyzerominor constant is used to initialize instances of
+** YYMINORTYPE objects to zero. */
+static const YYMINORTYPE yyzerominor = { 0 };
+
+/* Define the yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage.  For production
+** code the yytestcase() macro should be turned off.  But it is useful
+** for testing.
+*/
+#ifndef yytestcase
+# define yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token.  These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.  
+**
+** Suppose the action integer is N.  Then the action is determined as
+** follows
+**
+**   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
+**                                      token onto the stack and goto state N.
+**
+**   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
+**
+**   N == YYNSTATE+YYNRULE              A syntax error has occurred.
+**
+**   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
+**
+**   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
+**                                      slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+**      yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.  
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+**  yy_action[]        A single table containing all actions.
+**  yy_lookahead[]     A table containing the lookahead for each entry in
+**                     yy_action.  Used to detect hash collisions.
+**  yy_shift_ofst[]    For each state, the offset into yy_action for
+**                     shifting terminals.
+**  yy_reduce_ofst[]   For each state, the offset into yy_action for
+**                     shifting non-terminals after a reduce.
+**  yy_default[]       Default action for each state.
+*/
+%%
+
+/* The next table maps tokens into fallback tokens.  If a construct
+** like the following:
+** 
+**      %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+%%
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack.  Information stored includes:
+**
+**   +  The state number for the parser at this level of the stack.
+**
+**   +  The value of the token stored at this level of the stack.
+**      (In other words, the "major" token.)
+**
+**   +  The semantic value stored at this level of the stack.  This is
+**      the information used by the action routines in the grammar.
+**      It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+  YYACTIONTYPE stateno;  /* The state-number */
+  YYCODETYPE major;      /* The major token value.  This is the code
+                         ** number for the token at this stack level */
+  YYMINORTYPE minor;     /* The user-supplied minor token value.  This
+                         ** is the value of the token  */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+  int yyidx;                    /* Index of top element in stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+  int yyidxMax;                 /* Maximum value of yyidx */
+#endif
+  int yyerrcnt;                 /* Shifts left before out of the error */
+  ParseARG_SDECL                /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+  int yystksz;                  /* Current side of the stack */
+  yyStackEntry *yystack;        /* The parser's stack */
+#else
+  yyStackEntry yystack[YYSTACKDEPTH];  /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* 
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message.  Tracing is turned off
+** by making either argument NULL 
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+**      If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+**      line of trace output.  If NULL, then tracing is
+**      turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+  yyTraceFILE = TraceFILE;
+  yyTracePrompt = zTracePrompt;
+  if( yyTraceFILE==0 ) yyTracePrompt = 0;
+  else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required.  The following table supplies these names */
+static const char *const yyTokenName[] = { 
+%%
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void yyGrowStack(yyParser *p){
+  int newSize;
+  yyStackEntry *pNew;
+
+  newSize = p->yystksz*2 + 100;
+  pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+  if( pNew ){
+    p->yystack = pNew;
+    p->yystksz = newSize;
+#ifndef NDEBUG
+    if( yyTraceFILE ){
+      fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
+              yyTracePrompt, p->yystksz);
+    }
+#endif
+  }
+}
+#endif
+
+/* 
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser.  This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+void *ParseAlloc(void *(*mallocProc)(size_t)){
+  yyParser *pParser;
+  pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+  if( pParser ){
+    pParser->yyidx = -1;
+#ifdef YYTRACKMAXSTACKDEPTH
+    pParser->yyidxMax = 0;
+#endif
+#if YYSTACKDEPTH<=0
+    pParser->yystack = NULL;
+    pParser->yystksz = 0;
+    yyGrowStack(pParser);
+#endif
+  }
+  return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol.  The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(
+  yyParser *yypParser,    /* The parser */
+  YYCODETYPE yymajor,     /* Type code for object to destroy */
+  YYMINORTYPE *yypminor   /* The object to be destroyed */
+){
+  ParseARG_FETCH;
+  switch( yymajor ){
+    /* Here is inserted the actions which take place when a
+    ** terminal or non-terminal is destroyed.  This can happen
+    ** when the symbol is popped from the stack during a
+    ** reduce or during error processing or when a parser is 
+    ** being destroyed before it is finished parsing.
+    **
+    ** Note: during a reduce, the only symbols destroyed are those
+    ** which appear on the RHS of the rule, but which are not used
+    ** inside the C code.
+    */
+%%
+    default:  break;   /* If no destructor action specified: do nothing */
+  }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+  YYCODETYPE yymajor;
+  yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+  if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+  if( yyTraceFILE && pParser->yyidx>=0 ){
+    fprintf(yyTraceFILE,"%sPopping %s\n",
+      yyTracePrompt,
+      yyTokenName[yytos->major]);
+  }
+#endif
+  yymajor = yytos->major;
+  yy_destructor(pParser, yymajor, &yytos->minor);
+  pParser->yyidx--;
+  return yymajor;
+}
+
+/* 
+** Deallocate and destroy a parser.  Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li>  A pointer to the parser.  This should be a pointer
+**       obtained from ParseAlloc.
+** <li>  A pointer to a function used to reclaim memory obtained
+**       from malloc.
+** </ul>
+*/
+void ParseFree(
+  void *p,                    /* The parser to be deleted */
+  void (*freeProc)(void*)     /* Function used to reclaim memory */
+){
+  yyParser *pParser = (yyParser*)p;
+  if( pParser==0 ) return;
+  while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+  free(pParser->yystack);
+#endif
+  (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int ParseStackPeak(void *p){
+  yyParser *pParser = (yyParser*)p;
+  return pParser->yyidxMax;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+  yyParser *pParser,        /* The parser */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+  int stateno = pParser->yystack[pParser->yyidx].stateno;
+ 
+  if( stateno>YY_SHIFT_COUNT
+   || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
+    return yy_default[stateno];
+  }
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    if( iLookAhead>0 ){
+#ifdef YYFALLBACK
+      YYCODETYPE iFallback;            /* Fallback token */
+      if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+             && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+             yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+        }
+#endif
+        return yy_find_shift_action(pParser, iFallback);
+      }
+#endif
+#ifdef YYWILDCARD
+      {
+        int j = i - iLookAhead + YYWILDCARD;
+        if( 
+#if YY_SHIFT_MIN+YYWILDCARD<0
+          j>=0 &&
+#endif
+#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
+          j<YY_ACTTAB_COUNT &&
+#endif
+          yy_lookahead[j]==YYWILDCARD
+        ){
+#ifndef NDEBUG
+          if( yyTraceFILE ){
+            fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+               yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+          }
+#endif /* NDEBUG */
+          return yy_action[j];
+        }
+      }
+#endif /* YYWILDCARD */
+    }
+    return yy_default[stateno];
+  }else{
+    return yy_action[i];
+  }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+  int stateno,              /* Current state number */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+#ifdef YYERRORSYMBOL
+  if( stateno>YY_REDUCE_COUNT ){
+    return yy_default[stateno];
+  }
+#else
+  assert( stateno<=YY_REDUCE_COUNT );
+#endif
+  i = yy_reduce_ofst[stateno];
+  assert( i!=YY_REDUCE_USE_DFLT );
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+#ifdef YYERRORSYMBOL
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    return yy_default[stateno];
+  }
+#else
+  assert( i>=0 && i<YY_ACTTAB_COUNT );
+  assert( yy_lookahead[i]==iLookAhead );
+#endif
+  return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+   ParseARG_FETCH;
+   yypParser->yyidx--;
+#ifndef NDEBUG
+   if( yyTraceFILE ){
+     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+   }
+#endif
+   while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+   /* Here code is inserted which will execute if the parser
+   ** stack every overflows */
+%%
+   ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+  yyParser *yypParser,          /* The parser to be shifted */
+  int yyNewState,               /* The new state to shift in */
+  int yyMajor,                  /* The major token to shift in */
+  YYMINORTYPE *yypMinor         /* Pointer to the minor token to shift in */
+){
+  yyStackEntry *yytos;
+  yypParser->yyidx++;
+#ifdef YYTRACKMAXSTACKDEPTH
+  if( yypParser->yyidx>yypParser->yyidxMax ){
+    yypParser->yyidxMax = yypParser->yyidx;
+  }
+#endif
+#if YYSTACKDEPTH>0 
+  if( yypParser->yyidx>=YYSTACKDEPTH ){
+    yyStackOverflow(yypParser, yypMinor);
+    return;
+  }
+#else
+  if( yypParser->yyidx>=yypParser->yystksz ){
+    yyGrowStack(yypParser);
+    if( yypParser->yyidx>=yypParser->yystksz ){
+      yyStackOverflow(yypParser, yypMinor);
+      return;
+    }
+  }
+#endif
+  yytos = &yypParser->yystack[yypParser->yyidx];
+  yytos->stateno = (YYACTIONTYPE)yyNewState;
+  yytos->major = (YYCODETYPE)yyMajor;
+  yytos->minor = *yypMinor;
+#ifndef NDEBUG
+  if( yyTraceFILE && yypParser->yyidx>0 ){
+    int i;
+    fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+    fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+    for(i=1; i<=yypParser->yyidx; i++)
+      fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+    fprintf(yyTraceFILE,"\n");
+  }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+  YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
+  unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(yyParser*);  /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+  yyParser *yypParser,         /* The parser */
+  int yyruleno                 /* Number of the rule by which to reduce */
+){
+  int yygoto;                     /* The next state */
+  int yyact;                      /* The next action */
+  YYMINORTYPE yygotominor;        /* The LHS of the rule reduced */
+  yyStackEntry *yymsp;            /* The top of the parser's stack */
+  int yysize;                     /* Amount to pop the stack */
+  ParseARG_FETCH;
+  yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+  if( yyTraceFILE && yyruleno>=0 
+        && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+    fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+      yyRuleName[yyruleno]);
+  }
+#endif /* NDEBUG */
+
+  /* Silence complaints from purify about yygotominor being uninitialized
+  ** in some cases when it is copied into the stack after the following
+  ** switch.  yygotominor is uninitialized when a rule reduces that does
+  ** not set the value of its left-hand side nonterminal.  Leaving the
+  ** value of the nonterminal uninitialized is utterly harmless as long
+  ** as the value is never used.  So really the only thing this code
+  ** accomplishes is to quieten purify.  
+  **
+  ** 2007-01-16:  The wireshark project (www.wireshark.org) reports that
+  ** without this code, their parser segfaults.  I'm not sure what there
+  ** parser is doing to make this happen.  This is the second bug report
+  ** from wireshark this week.  Clearly they are stressing Lemon in ways
+  ** that it has not been previously stressed...  (SQLite ticket #2172)
+  */
+  /*memset(&yygotominor, 0, sizeof(yygotominor));*/
+  yygotominor = yyzerominor;
+
+
+  switch( yyruleno ){
+  /* Beginning here are the reduction cases.  A typical example
+  ** follows:
+  **   case 0:
+  **  #line <lineno> <grammarfile>
+  **     { ... }           // User supplied code
+  **  #line <lineno> <thisfile>
+  **     break;
+  */
+%%
+  };
+  yygoto = yyRuleInfo[yyruleno].lhs;
+  yysize = yyRuleInfo[yyruleno].nrhs;
+  yypParser->yyidx -= yysize;
+  yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
+  if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+    /* If we are not debugging and the reduce action popped at least
+    ** one element off the stack, then we can push the new element back
+    ** onto the stack here, and skip the stack overflow test in yy_shift().
+    ** That gives a significant speed improvement. */
+    if( yysize ){
+      yypParser->yyidx++;
+      yymsp -= yysize-1;
+      yymsp->stateno = (YYACTIONTYPE)yyact;
+      yymsp->major = (YYCODETYPE)yygoto;
+      yymsp->minor = yygotominor;
+    }else
+#endif
+    {
+      yy_shift(yypParser,yyact,yygoto,&yygotominor);
+    }
+  }else{
+    assert( yyact == YYNSTATE + YYNRULE + 1 );
+    yy_accept(yypParser);
+  }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef YYNOERRORRECOVERY
+static void yy_parse_failed(
+  yyParser *yypParser           /* The parser */
+){
+  ParseARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+  }
+#endif
+  while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+  /* Here code is inserted which will be executed whenever the
+  ** parser fails */
+%%
+  ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+  yyParser *yypParser,           /* The parser */
+  int yymajor,                   /* The major type of the error token */
+  YYMINORTYPE yyminor            /* The minor type of the error token */
+){
+  ParseARG_FETCH;
+#define TOKEN (yyminor.yy0)
+%%
+  ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+  yyParser *yypParser           /* The parser */
+){
+  ParseARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+  }
+#endif
+  while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+  /* Here code is inserted which will be executed whenever the
+  ** parser accepts */
+%%
+  ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "ParseAlloc" which describes the current state of the parser.
+** The second argument is the major token number.  The third is
+** the minor token.  The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void Parse(
+  void *yyp,                   /* The parser */
+  int yymajor,                 /* The major token code number */
+  ParseTOKENTYPE yyminor       /* The value for the token */
+  ParseARG_PDECL               /* Optional %extra_argument parameter */
+){
+  YYMINORTYPE yyminorunion;
+  int yyact;            /* The parser action. */
+  int yyendofinput;     /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
+  int yyerrorhit = 0;   /* True if yymajor has invoked an error */
+#endif
+  yyParser *yypParser;  /* The parser */
+
+  /* (re)initialize the parser, if necessary */
+  yypParser = (yyParser*)yyp;
+  if( yypParser->yyidx<0 ){
+#if YYSTACKDEPTH<=0
+    if( yypParser->yystksz <=0 ){
+      /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
+      yyminorunion = yyzerominor;
+      yyStackOverflow(yypParser, &yyminorunion);
+      return;
+    }
+#endif
+    yypParser->yyidx = 0;
+    yypParser->yyerrcnt = -1;
+    yypParser->yystack[0].stateno = 0;
+    yypParser->yystack[0].major = 0;
+  }
+  yyminorunion.yy0 = yyminor;
+  yyendofinput = (yymajor==0);
+  ParseARG_STORE;
+
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+  }
+#endif
+
+  do{
+    yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
+    if( yyact<YYNSTATE ){
+      assert( !yyendofinput );  /* Impossible to shift the $ token */
+      yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+      yypParser->yyerrcnt--;
+      yymajor = YYNOCODE;
+    }else if( yyact < YYNSTATE + YYNRULE ){
+      yy_reduce(yypParser,yyact-YYNSTATE);
+    }else{
+      assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
+      int yymx;
+#endif
+#ifndef NDEBUG
+      if( yyTraceFILE ){
+        fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+      }
+#endif
+#ifdef YYERRORSYMBOL
+      /* A syntax error has occurred.
+      ** The response to an error depends upon whether or not the
+      ** grammar defines an error token "ERROR".  
+      **
+      ** This is what we do if the grammar does define ERROR:
+      **
+      **  * Call the %syntax_error function.
+      **
+      **  * Begin popping the stack until we enter a state where
+      **    it is legal to shift the error symbol, then shift
+      **    the error symbol.
+      **
+      **  * Set the error count to three.
+      **
+      **  * Begin accepting and shifting new tokens.  No new error
+      **    processing will occur until three tokens have been
+      **    shifted successfully.
+      **
+      */
+      if( yypParser->yyerrcnt<0 ){
+        yy_syntax_error(yypParser,yymajor,yyminorunion);
+      }
+      yymx = yypParser->yystack[yypParser->yyidx].major;
+      if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+             yyTracePrompt,yyTokenName[yymajor]);
+        }
+#endif
+        yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
+        yymajor = YYNOCODE;
+      }else{
+         while(
+          yypParser->yyidx >= 0 &&
+          yymx != YYERRORSYMBOL &&
+          (yyact = yy_find_reduce_action(
+                        yypParser->yystack[yypParser->yyidx].stateno,
+                        YYERRORSYMBOL)) >= YYNSTATE
+        ){
+          yy_pop_parser_stack(yypParser);
+        }
+        if( yypParser->yyidx < 0 || yymajor==0 ){
+          yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+          yy_parse_failed(yypParser);
+          yymajor = YYNOCODE;
+        }else if( yymx!=YYERRORSYMBOL ){
+          YYMINORTYPE u2;
+          u2.YYERRSYMDT = 0;
+          yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+        }
+      }
+      yypParser->yyerrcnt = 3;
+      yyerrorhit = 1;
+#elif defined(YYNOERRORRECOVERY)
+      /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
+      ** do any kind of error recovery.  Instead, simply invoke the syntax
+      ** error routine and continue going as if nothing had happened.
+      **
+      ** Applications can set this macro (for example inside %include) if
+      ** they intend to abandon the parse upon the first syntax error seen.
+      */
+      yy_syntax_error(yypParser,yymajor,yyminorunion);
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      yymajor = YYNOCODE;
+      
+#else  /* YYERRORSYMBOL is not defined */
+      /* This is what we do if the grammar does not define ERROR:
+      **
+      **  * Report an error message, and throw away the input token.
+      **
+      **  * If the input token is $, then fail the parse.
+      **
+      ** As before, subsequent error messages are suppressed until
+      ** three input tokens have been successfully shifted.
+      */
+      if( yypParser->yyerrcnt<=0 ){
+        yy_syntax_error(yypParser,yymajor,yyminorunion);
+      }
+      yypParser->yyerrcnt = 3;
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      if( yyendofinput ){
+        yy_parse_failed(yypParser);
+      }
+      yymajor = YYNOCODE;
+#endif
+    }
+  }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+  return;
+}

+ 149 - 0
unql/src/memory.c

@@ -0,0 +1,149 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Pooled memory allocation
+*/
+#include "xjd1Int.h"
+
+static void *dflt_malloc(int N){ return malloc(N); }
+static void dflt_free(void *p){ free(p); }
+static void *dflt_realloc(void *p, int N){ return realloc(p, N); }
+
+static struct {
+  void *(*xMalloc)(int);
+  void *(*xRealloc)(void *, int);
+  void (*xFree)(void *);
+} global = {
+  dflt_malloc,
+  dflt_realloc,
+  dflt_free
+};
+
+void xjd1_configure_malloc(
+  void *(*xMalloc)(int),
+  void *(*xRealloc)(void *, int),
+  void (*xFree)(void *)
+){
+  global.xMalloc = xMalloc;
+  global.xRealloc = xRealloc;
+  global.xFree = xFree;
+}
+
+void *xjd1_malloc(int N){
+  return global.xMalloc(N);
+}
+void xjd1_free(void *p){
+  global.xFree(p);
+}
+void *xjd1_realloc(void *p, int N){
+  return global.xRealloc(p, N);
+}
+
+void *xjd1MallocZero(int N){
+  void *pRet;
+  pRet = global.xMalloc(N);
+  if( pRet ) memset(pRet, 0, N);
+  return pRet;
+}
+
+
+/*
+** Create a new memory allocation pool.  Return a pointer to the
+** memory pool or NULL on OOM error.
+*/
+Pool *xjd1PoolNew(void){
+  Pool *p = xjd1_malloc( sizeof(*p) );
+  if( p ) memset(p, 0, sizeof(*p));
+  return p;
+}
+
+/*
+** Free a memory allocation pool allocated using xjd1PoolNew().
+*/
+void xjd1PoolDelete(Pool *p){
+  if( p ){
+    xjd1PoolClear(p);
+    xjd1_free(p);
+  }
+}
+
+/*
+** Clear a memory allocation pool.  That is to say, xjd1_free all the
+** memory allocations associated with the pool, though do not xjd1_free
+** the memory pool itself.
+*/
+void xjd1PoolClear(Pool *p){
+  PoolChunk *pChunk, *pNext;
+  for(pChunk = p->pChunk; pChunk; pChunk = pNext){
+    pNext = pChunk->pNext;
+    xjd1_free(pChunk);
+  }
+  memset(p, 0, sizeof(*p));
+}
+
+/*
+** Allocate N bytes of memory from the memory allocation pool.
+*/
+#define POOL_CHUNK_SIZE 3000
+void *xjd1PoolMalloc(Pool *p, int N){
+  N = (N+7)&~7;
+  if( N>POOL_CHUNK_SIZE/4 ){
+    PoolChunk *pChunk = xjd1_malloc( N + 8 );
+    if( pChunk==0 ) return 0;
+    pChunk->pNext = p->pChunk;
+    p->pChunk = pChunk;
+    return &((char*)pChunk)[8];
+  }else{
+    void *x;
+    if( p->nSpace<N ){
+      PoolChunk *pChunk = xjd1_malloc( POOL_CHUNK_SIZE + 8 );
+      if( pChunk==0 ) return 0;
+      pChunk->pNext = p->pChunk;
+      p->pChunk = pChunk;
+      p->pSpace = (char*)pChunk;
+      p->pSpace += 8;
+      p->nSpace = POOL_CHUNK_SIZE;
+    }
+    x = p->pSpace;
+    p->pSpace += N;
+    p->nSpace -= N;
+    return x;
+  }
+}
+void *xjd1PoolMallocZero(Pool *p, int N){
+  void *x = xjd1PoolMalloc(p,N);
+  if( x ) memset(x, 0, (N+7)&~7);
+  return x;
+}
+
+/*
+** Create a duplicate of a string using a memory pool.  Or if pPool==0
+** obtain the memory for the duplicate from xjd1_malloc().
+*/
+char *xjd1PoolDup(Pool *pPool, const char *z, int n){
+  char *zOut;
+  if( n<0 ) n = xjd1Strlen30(z);
+  if( pPool ){
+    zOut = xjd1PoolMalloc(pPool, n+1);
+  }else{
+    zOut = xjd1_malloc( n+1 );
+  }
+  if( zOut ){
+     memcpy(zOut, z, n);
+     zOut[n] = 0;
+  }
+  return zOut;
+}

+ 2196 - 0
unql/src/parse.c

@@ -0,0 +1,2196 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
+#include <stdio.h>
+#line 46 "parse.y"
+
+#include "xjd1Int.h"
+
+/*
+** Disable all error recovery processing in the parser push-down
+** automaton.
+*/
+#define YYNOERRORRECOVERY 1
+
+/*
+** Make yytestcase() be a no-op
+*/
+#define yytestcase(X)
+
+#line 70 "parse.y"
+
+  /* A JSON literal for a real number */
+  static JsonNode *jsonReal(Parse *p, Token *pTok){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = XJD1_REAL;
+      pNew->u.r = atof(pTok->z);
+    }
+    return pNew;
+  }
+
+  /* Convert a token into a zero-terminated string */
+  static char *tokenStr(Parse *p, Token *pTok){
+    char *z;
+    if( pTok ){
+      z = xjd1PoolDup(p->pPool, pTok->z, pTok->n);
+      if( z && z[0]=='"' ) xjd1DequoteString(z, pTok->n);
+    }else{
+      z = 0;
+    }
+    return z;
+  }
+
+  /* A JSON literal for a string */
+  static JsonNode *jsonString(Parse *p, Token *pTok){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = XJD1_STRING;
+      pNew->u.z = tokenStr(p, pTok);
+    }
+    return pNew;
+  }
+
+  /* A JSON literal for a boolean or NULL */
+  static JsonNode *jsonType(Parse *p, int eJType){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = eJType;
+    }
+    return pNew;
+  }
+#line 134 "parse.y"
+
+  /* Generate an Expr object from an identifer token */
+  static Expr *idExpr(Parse *p, Token *pTok){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_ID;
+      pNew->eClass = XJD1_EXPR_TK;
+      pNew->u.id.zId = tokenStr(p, pTok);
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a binary operator on two
+  ** other Expr objects. */
+  static Expr *biExpr(Parse *p, Expr *pLeft, int eOp, Expr *pRight){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = eOp; 
+      pNew->eClass = XJD1_EXPR_BI;
+      pNew->u.bi.pLeft = pLeft;
+      pNew->u.bi.pRight = pRight;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a tertiary operator */
+  static Expr *triExpr(Parse *p, Expr *pTest, Expr *pTrue, Expr *pFalse){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_QM; 
+      pNew->eClass = XJD1_EXPR_TRI;
+      pNew->u.tri.pTest = pTest;
+      pNew->u.tri.pIfTrue = pTrue;
+      pNew->u.tri.pIfFalse = pFalse;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object for an lvalue */
+  static Expr *lvalueExpr(Parse *p, Expr *pLeft, Token *pId){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_DOT; 
+      pNew->eClass = XJD1_EXPR_LVALUE;
+      pNew->u.lvalue.pLeft = pLeft;
+      pNew->u.lvalue.zId = tokenStr(p, pId);
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a function call. */
+  static Expr *funcExpr(Parse *p, Token *pFName, ExprList *pArgs){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_FUNCTION; 
+      pNew->eClass = XJD1_EXPR_FUNC;
+      pNew->u.func.zFName = tokenStr(p, pFName);
+      if( pArgs==0 ){
+        pArgs = xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+      pNew->u.func.args = pArgs;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a subquery. */
+  static Expr *subqExpr(Parse *p, Query *pQuery){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_SELECT;
+      pNew->eClass = XJD1_EXPR_Q;
+      pNew->u.subq.p = pQuery;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a structure */
+  static Expr *stExpr(Parse *p, ExprList *pList){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_STRUCT;
+      pNew->eClass = XJD1_EXPR_STRUCT;
+      if( pList ){
+        pNew->u.st = pList;
+      }else{
+        pNew->u.st = (ExprList *)xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is an array7 */
+  static Expr *arExpr(Parse *p, ExprList *pList){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_ARRAY;
+      pNew->eClass = XJD1_EXPR_ARRAY;
+      if( pList ){
+        pNew->u.ar = pList;
+      }else{
+        pNew->u.ar = (ExprList *)xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is JSON value literal */
+  static Expr *jsonExpr(Parse *p, JsonNode *pNode){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_JVALUE;
+      pNew->eClass = XJD1_EXPR_JSON;
+      pNew->u.json.p = pNode;
+    }
+    return pNew;
+  }
+
+  /* Append a new expression to an expression list.  Allocate the
+  ** expression list object if necessary. */
+  static ExprList *apndExpr(Parse *p, ExprList *pList, Expr *pExpr, Token *pT){
+    ExprItem *pItem;
+    if( pList==0 ){
+      pList = xjd1PoolMallocZero(p->pPool, sizeof(*pList)+4*sizeof(ExprItem));
+      if( pList==0 ) return 0;
+      pList->nEAlloc = 0;
+    }
+    if( pList->nEAlloc<=pList->nEItem ){
+      ExprItem *pNew;
+      int n = pList->nEAlloc*4;
+      if( n==0 ) n = 10;
+      pNew = xjd1PoolMalloc(p->pPool, sizeof(ExprItem)*n);
+      if( pNew==0 ) return pList;
+      memcpy(pNew, pList->apEItem, pList->nEItem*sizeof(ExprItem));
+      pList->nEAlloc = n;
+      pList->apEItem = pNew;
+    }
+    pItem = &pList->apEItem[pList->nEItem++];
+    pItem->zAs = tokenStr(p, pT);
+    pItem->pExpr = pExpr;
+    return pList;
+  }
+#line 350 "parse.y"
+
+  /* The value of a LIMIT ... OFFSET ... clause. */
+  typedef struct LimitOffset {
+    Expr *pLimit;
+    Expr *pOffset;
+  } LimitOffset;
+
+  /* The value of a GROUP BY ... HAVING ... clause */
+  typedef struct GroupByHaving {
+    ExprList *pGroupBy;
+    Expr *pHaving;
+  } GroupByHaving;
+
+  /* Construct a simple query object */
+  static Query *simpleQuery(
+    Parse *p,
+    int isDistinct,
+    Expr *pRes,
+    const char *zAs,
+    DataSrc *pFrom,
+    Expr *pWhere,
+    GroupByHaving *pGroupBy
+  ){
+    Query *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eQType = TK_SELECT;
+      pNew->u.simple.isDistinct = isDistinct;
+      pNew->u.simple.pRes = pRes;
+      pNew->zAs = zAs;
+      pNew->u.simple.pFrom = pFrom;
+      pNew->u.simple.pWhere = pWhere;
+      pNew->u.simple.pGroupBy = pGroupBy ? pGroupBy->pGroupBy : 0;
+      pNew->u.simple.pHaving = pGroupBy ? pGroupBy->pHaving : 0;
+    }
+    return pNew;
+  }
+
+  /* Construct a compound query object */
+  static Query *compoundQuery(
+    Parse *p,
+    Query *pLeft,
+    int eOp,
+    Query *pRight
+  ){
+    Query *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eQType = eOp;
+      pNew->u.compound.pLeft = pLeft;
+      pNew->u.compound.pRight = pRight;
+      pNew->zAs = pLeft->zAs;
+    }
+    return pNew;
+  }
+#line 461 "parse.y"
+
+  /* Create a new data source that is a named table */
+  static DataSrc *pathDataSrc(Parse *p, Expr *pPath, Token *pAs){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      if( pPath ){
+        if( pPath->eType==TK_ID ){
+          pNew->eDSType = TK_ID;
+          pNew->u.tab.zName = pPath->u.id.zId;
+        }else{
+          pNew->eDSType = TK_DOT;
+          pNew->u.path.pPath = pPath;
+        }
+        pNew->zAs = tokenStr(p, pAs);
+      }else{
+        pNew->eDSType = TK_ID;
+        pNew->u.tab.zName = tokenStr(p,pAs);
+      }
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that is a join */
+  static DataSrc *joinDataSrc(Parse *p, DataSrc *pLeft, DataSrc *pRight){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_COMMA;
+      pNew->u.join.pLeft = pLeft;
+      pNew->u.join.pRight = pRight;
+    }
+    return pNew;
+  }
+
+  /* Create a new subquery data source */
+  static DataSrc *subqDataSrc(Parse *p, Query *pSubq, Token *pAs){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_SELECT;
+      pNew->u.subq.q = pSubq;
+      pNew->zAs = tokenStr(p, pAs);
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that is a FLATTEN or EACH operator */
+  static DataSrc *flattenDataSrc(
+    Parse *p,
+    DataSrc *pLeft,
+    Token *pOp,
+    Expr *pPath,
+    Expr *pAs
+  ){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_FLATTENOP;
+      pNew->u.flatten.pNext = pLeft;
+      pNew->u.flatten.cOpName = pOp->z[0];
+      pNew->u.flatten.pExpr = pPath;
+      pNew->u.flatten.pAs = (pAs ? pAs : pPath);
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that represents an empty FROM clause.
+  ** This is used for queries of the form "SELECT <expr>". It returns a
+  ** single object with no properties.  
+  */
+  static DataSrc *nullDataSrc(Parse *p){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_NULL;
+    }
+    return pNew;
+  }
+#line 690 "parse.y"
+
+  static Command *makePrag(Parse *p, Token *pName, Expr *pValue){
+    Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eCmdType = TK_PRAGMA;
+      pNew->u.prag.zName = tokenStr(p, pName);
+      pNew->u.prag.pValue = pValue;
+    }
+    return pNew;
+  }
+#line 347 "parse.c"
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/* 
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands. 
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+**    YYCODETYPE         is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 terminals
+**                       and nonterminals.  "int" is used otherwise.
+**    YYNOCODE           is a number of type YYCODETYPE which corresponds
+**                       to no legal terminal or nonterminal number.  This
+**                       number is used to fill in empty slots of the hash 
+**                       table.
+**    YYFALLBACK         If defined, this indicates that one or more tokens
+**                       have fall-back values which should be used if the
+**                       original value of the token will not parse.
+**    YYACTIONTYPE       is the data type used for storing terminal
+**                       and nonterminal numbers.  "unsigned char" is
+**                       used if there are fewer than 250 rules and
+**                       states combined.  "int" is used otherwise.
+**    xjd1ParserTOKENTYPE     is the data type used for minor tokens given 
+**                       directly to the parser from the tokenizer.
+**    YYMINORTYPE        is the data type used for all minor tokens.
+**                       This is typically a union of many types, one of
+**                       which is xjd1ParserTOKENTYPE.  The entry in the union
+**                       for base tokens is called "yy0".
+**    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
+**                       zero the stack is dynamically sized using realloc()
+**    xjd1ParserARG_SDECL     A static variable declaration for the %extra_argument
+**    xjd1ParserARG_PDECL     A parameter declaration for the %extra_argument
+**    xjd1ParserARG_STORE     Code to store %extra_argument into yypParser
+**    xjd1ParserARG_FETCH     Code to extract %extra_argument from yypParser
+**    YYNSTATE           the combined number of states.
+**    YYNRULE            the number of rules in the grammar
+**    YYERRORSYMBOL      is the code number of the error symbol.  If not
+**                       defined, then do no error processing.
+*/
+#define YYCODETYPE unsigned char
+#define YYNOCODE 120
+#define YYACTIONTYPE unsigned short int
+#define xjd1ParserTOKENTYPE Token
+typedef union {
+  int yyinit;
+  xjd1ParserTOKENTYPE yy0;
+  LimitOffset yy17;
+  ExprList* yy28;
+  int yy60;
+  Query* yy89;
+  ExprItem yy90;
+  Command* yy156;
+  JsonNode* yy181;
+  GroupByHaving yy186;
+  Expr* yy216;
+  DataSrc* yy225;
+} YYMINORTYPE;
+#ifndef YYSTACKDEPTH
+#define YYSTACKDEPTH 100
+#endif
+#define xjd1ParserARG_SDECL Parse *p;
+#define xjd1ParserARG_PDECL ,Parse *p
+#define xjd1ParserARG_FETCH Parse *p = yypParser->p
+#define xjd1ParserARG_STORE yypParser->p = p
+#define YYNSTATE 219
+#define YYNRULE 114
+#define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
+
+/* The yyzerominor constant is used to initialize instances of
+** YYMINORTYPE objects to zero. */
+static const YYMINORTYPE yyzerominor = { 0 };
+
+/* Define the yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage.  For production
+** code the yytestcase() macro should be turned off.  But it is useful
+** for testing.
+*/
+#ifndef yytestcase
+# define yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token.  These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.  
+**
+** Suppose the action integer is N.  Then the action is determined as
+** follows
+**
+**   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
+**                                      token onto the stack and goto state N.
+**
+**   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
+**
+**   N == YYNSTATE+YYNRULE              A syntax error has occurred.
+**
+**   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
+**
+**   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
+**                                      slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+**      yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.  
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol.  If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+**  yy_action[]        A single table containing all actions.
+**  yy_lookahead[]     A table containing the lookahead for each entry in
+**                     yy_action.  Used to detect hash collisions.
+**  yy_shift_ofst[]    For each state, the offset into yy_action for
+**                     shifting terminals.
+**  yy_reduce_ofst[]   For each state, the offset into yy_action for
+**                     shifting non-terminals after a reduce.
+**  yy_default[]       Default action for each state.
+*/
+#define YY_ACTTAB_COUNT (787)
+static const YYACTIONTYPE yy_action[] = {
+ /*     0 */    31,   43,   46,   38,   39,   40,   33,   32,   41,   41,
+ /*    10 */    41,   41,   34,   34,   42,   42,   42,   42,   37,   37,
+ /*    20 */    37,   36,   36,   35,   35,   35,  163,   47,  115,   38,
+ /*    30 */    39,   40,   33,   32,   41,   41,   41,   41,   34,   34,
+ /*    40 */    42,   42,   42,   42,   37,   37,   37,   36,   36,   35,
+ /*    50 */    35,   35,  202,  201,  115,   35,   35,   35,  146,  109,
+ /*    60 */   115,   31,   43,   46,   38,   39,   40,   33,   32,   41,
+ /*    70 */    41,   41,   41,   34,   34,   42,   42,   42,   42,   37,
+ /*    80 */    37,   37,   36,   36,   35,   35,   35,  219,  162,  115,
+ /*    90 */     4,    7,    6,  211,   31,   43,   46,   38,   39,   40,
+ /*   100 */    33,   32,   41,   41,   41,   41,   34,   34,   42,   42,
+ /*   110 */    42,   42,   37,   37,   37,   36,   36,   35,   35,   35,
+ /*   120 */   165,  162,  115,   36,   36,   35,   35,   35,  212,   30,
+ /*   130 */   115,   31,   43,   46,   38,   39,   40,   33,   32,   41,
+ /*   140 */    41,   41,   41,   34,   34,   42,   42,   42,   42,   37,
+ /*   150 */    37,   37,   36,   36,   35,   35,   35,   59,   45,  115,
+ /*   160 */    37,   37,   37,   36,   36,   35,   35,   35,  205,  206,
+ /*   170 */   115,  210,  119,   86,  209,  116,  117,   20,   31,   43,
+ /*   180 */    46,   38,   39,   40,   33,   32,   41,   41,   41,   41,
+ /*   190 */    34,   34,   42,   42,   42,   42,   37,   37,   37,   36,
+ /*   200 */    36,   35,   35,   35,  115,    9,  115,   31,   43,   46,
+ /*   210 */    38,   39,   40,   33,   32,   41,   41,   41,   41,   34,
+ /*   220 */    34,   42,   42,   42,   42,   37,   37,   37,   36,   36,
+ /*   230 */    35,   35,   35,   24,  186,  115,  187,   34,   34,   42,
+ /*   240 */    42,   42,   42,   37,   37,   37,   36,   36,   35,   35,
+ /*   250 */    35,  110,  151,  115,  147,  137,  109,   12,   13,    2,
+ /*   260 */    23,   31,   43,   46,   38,   39,   40,   33,   32,   41,
+ /*   270 */    41,   41,   41,   34,   34,   42,   42,   42,   42,   37,
+ /*   280 */    37,   37,   36,   36,   35,   35,   35,  208,    6,  115,
+ /*   290 */    14,   44,  210,  119,   76,  210,  119,   91,  156,  167,
+ /*   300 */   199,   53,  111,  199,  197,  152,  196,  108,   31,   43,
+ /*   310 */    46,   38,   39,   40,   33,   32,   41,   41,   41,   41,
+ /*   320 */    34,   34,   42,   42,   42,   42,   37,   37,   37,   36,
+ /*   330 */    36,   35,   35,   35,   56,    8,  115,   46,   38,   39,
+ /*   340 */    40,   33,   32,   41,   41,   41,   41,   34,   34,   42,
+ /*   350 */    42,   42,   42,   37,   37,   37,   36,   36,   35,   35,
+ /*   360 */    35,  163,   47,  115,  135,   22,  218,  217,  216,  215,
+ /*   370 */   214,  213,   40,   33,   32,   41,   41,   41,   41,   34,
+ /*   380 */    34,   42,   42,   42,   42,   37,   37,   37,   36,   36,
+ /*   390 */    35,   35,   35,   26,   27,  115,   21,  135,   28,   29,
+ /*   400 */    18,  164,  184,   10,  145,   55,  163,   47,  207,    3,
+ /*   410 */   166,  334,  210,  119,   91,   57,  193,   49,  182,   68,
+ /*   420 */   199,   53,  111,  194,  192,  210,  119,   91,  191,  133,
+ /*   430 */   132,  131,  130,  199,  197,  198,  140,  127,  125,   61,
+ /*   440 */   142,  207,  122,  189,  139,   16,  169,  168,  120,  193,
+ /*   450 */    39,   40,   33,   32,   41,   41,   41,   41,   34,   34,
+ /*   460 */    42,   42,   42,   42,   37,   37,   37,   36,   36,   35,
+ /*   470 */    35,   35,  188,  142,  115,   33,   32,   41,   41,   41,
+ /*   480 */    41,   34,   34,   42,   42,   42,   42,   37,   37,   37,
+ /*   490 */    36,   36,   35,   35,   35,  183,   19,  115,   67,  181,
+ /*   500 */   218,  217,  216,  215,  214,  213,  176,  210,  119,   91,
+ /*   510 */   210,  119,   91,  141,  128,  199,   53,  111,  199,  197,
+ /*   520 */   136,  210,  119,   91,  272,  272,  272,   26,   27,  199,
+ /*   530 */   197,  153,   28,   29,  180,  164,  155,   10,  179,   55,
+ /*   540 */   210,  119,   90,    3,   65,  134,  174,  149,    5,   57,
+ /*   550 */    63,  218,  217,  216,  215,  214,  213,  210,  119,   91,
+ /*   560 */    50,  291,  291,  170,   17,  199,   53,  111,  210,  119,
+ /*   570 */    90,   58,  121,  148,  204,  149,  291,  112,   26,   27,
+ /*   580 */   210,  119,   52,   28,   29,   70,  164,   48,   10,   25,
+ /*   590 */    55,  210,  119,   74,    3,  143,  113,  143,  113,   69,
+ /*   600 */    57,  195,   20,  154,  150,  190,   71,  210,  119,   78,
+ /*   610 */   138,   54,  114,  210,  119,   96,  210,  119,   85,  210,
+ /*   620 */   119,   94,  210,  119,   95,  210,  119,  104,   66,  177,
+ /*   630 */    15,  129,  178,  218,  217,  216,  215,  214,  213,   64,
+ /*   640 */   210,  119,  102,  210,  119,   99,  210,  119,   98,  126,
+ /*   650 */   210,  119,   97,  175,  210,  119,  105,  210,  119,  106,
+ /*   660 */    26,   27,  210,  119,  161,   28,   29,   62,  164,  173,
+ /*   670 */    10,  185,   55,  210,  119,  103,    3,  124,  123,  218,
+ /*   680 */   217,  216,  215,  214,  213,  210,  119,  101,  171,  210,
+ /*   690 */   119,  100,  210,  119,   77,  210,  119,   93,  210,  119,
+ /*   700 */   160,  210,  119,  159,  107,  172,   26,   27,  210,  119,
+ /*   710 */   158,   28,   29,    1,  164,  118,   10,   60,   55,   11,
+ /*   720 */   203,  144,    3,  210,  119,  157,   72,  335,   57,  335,
+ /*   730 */   200,  218,  217,  216,  215,  214,  213,  210,  119,   75,
+ /*   740 */   210,  119,   92,  210,  119,   51,  210,  119,   89,  210,
+ /*   750 */   119,   88,  210,  119,   87,  210,  119,   84,   26,   27,
+ /*   760 */   210,  119,   83,   28,   29,  335,  164,  335,   10,  335,
+ /*   770 */    55,  210,  119,   82,    3,  210,  119,   81,  210,  119,
+ /*   780 */    80,  210,  119,   79,  210,  119,   73,
+};
+static const YYCODETYPE yy_lookahead[] = {
+ /*     0 */     8,    9,   10,   11,   12,   13,   14,   15,   16,   17,
+ /*    10 */    18,   19,   20,   21,   22,   23,   24,   25,   26,   27,
+ /*    20 */    28,   29,   30,   31,   32,   33,   38,   39,   36,   11,
+ /*    30 */    12,   13,   14,   15,   16,   17,   18,   19,   20,   21,
+ /*    40 */    22,   23,   24,   25,   26,   27,   28,   29,   30,   31,
+ /*    50 */    32,   33,   60,   61,   36,   31,   32,   33,  108,  109,
+ /*    60 */    36,    8,    9,   10,   11,   12,   13,   14,   15,   16,
+ /*    70 */    17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
+ /*    80 */    27,   28,   29,   30,   31,   32,   33,    0,    4,   36,
+ /*    90 */    47,   48,   49,   40,    8,    9,   10,   11,   12,   13,
+ /*   100 */    14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
+ /*   110 */    24,   25,   26,   27,   28,   29,   30,   31,   32,   33,
+ /*   120 */     1,   37,   36,   29,   30,   31,   32,   33,   37,   43,
+ /*   130 */    36,    8,    9,   10,   11,   12,   13,   14,   15,   16,
+ /*   140 */    17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
+ /*   150 */    27,   28,   29,   30,   31,   32,   33,   44,   43,   36,
+ /*   160 */    26,   27,   28,   29,   30,   31,   32,   33,   46,   46,
+ /*   170 */    36,   88,   89,   90,   42,   92,   44,   64,    8,    9,
+ /*   180 */    10,   11,   12,   13,   14,   15,   16,   17,   18,   19,
+ /*   190 */    20,   21,   22,   23,   24,   25,   26,   27,   28,   29,
+ /*   200 */    30,   31,   32,   33,   36,   45,   36,    8,    9,   10,
+ /*   210 */    11,   12,   13,   14,   15,   16,   17,   18,   19,   20,
+ /*   220 */    21,   22,   23,   24,   25,   26,   27,   28,   29,   30,
+ /*   230 */    31,   32,   33,   63,   50,   36,   52,   20,   21,   22,
+ /*   240 */    23,   24,   25,   26,   27,   28,   29,   30,   31,   32,
+ /*   250 */    33,   37,   53,   36,  107,  108,  109,   57,   45,   45,
+ /*   260 */    44,    8,    9,   10,   11,   12,   13,   14,   15,   16,
+ /*   270 */    17,   18,   19,   20,   21,   22,   23,   24,   25,   26,
+ /*   280 */    27,   28,   29,   30,   31,   32,   33,   40,   49,   36,
+ /*   290 */    77,   44,   88,   89,   90,   88,   89,   90,   94,   46,
+ /*   300 */    96,   97,   98,   96,   97,   98,   37,   89,    8,    9,
+ /*   310 */    10,   11,   12,   13,   14,   15,   16,   17,   18,   19,
+ /*   320 */    20,   21,   22,   23,   24,   25,   26,   27,   28,   29,
+ /*   330 */    30,   31,   32,   33,  116,   57,   36,   10,   11,   12,
+ /*   340 */    13,   14,   15,   16,   17,   18,   19,   20,   21,   22,
+ /*   350 */    23,   24,   25,   26,   27,   28,   29,   30,   31,   32,
+ /*   360 */    33,   38,   39,   36,    4,   44,    2,    3,    4,    5,
+ /*   370 */     6,    7,   13,   14,   15,   16,   17,   18,   19,   20,
+ /*   380 */    21,   22,   23,   24,   25,   26,   27,   28,   29,   30,
+ /*   390 */    31,   32,   33,   29,   30,   36,   58,   37,   34,   35,
+ /*   400 */    77,   37,   42,   39,   55,   41,   38,   39,    4,   45,
+ /*   410 */    86,   87,   88,   89,   90,   51,    4,   44,   94,   45,
+ /*   420 */    96,   97,   98,   46,   37,   88,   89,   90,   40,   65,
+ /*   430 */    66,   67,   68,   96,   97,   98,   46,   73,   74,   75,
+ /*   440 */     4,   37,  118,   37,   53,   77,   82,   83,   84,   37,
+ /*   450 */    12,   13,   14,   15,   16,   17,   18,   19,   20,   21,
+ /*   460 */    22,   23,   24,   25,   26,   27,   28,   29,   30,   31,
+ /*   470 */    32,   33,   37,   37,   36,   14,   15,   16,   17,   18,
+ /*   480 */    19,   20,   21,   22,   23,   24,   25,   26,   27,   28,
+ /*   490 */    29,   30,   31,   32,   33,   46,   43,   36,   69,   37,
+ /*   500 */     2,    3,    4,    5,    6,    7,   72,   88,   89,   90,
+ /*   510 */    88,   89,   90,   94,   71,   96,   97,   98,   96,   97,
+ /*   520 */    98,   88,   89,   90,   47,   48,   49,   29,   30,   96,
+ /*   530 */    97,   98,   34,   35,   37,   37,   59,   39,   37,   41,
+ /*   540 */    88,   89,   90,   45,   69,   93,   72,   95,   50,   51,
+ /*   550 */    54,    2,    3,    4,    5,    6,    7,   88,   89,   90,
+ /*   560 */    76,   38,   39,   94,   79,   96,   97,   98,   88,   89,
+ /*   570 */    90,   80,   79,   93,  101,   95,   53,   37,   29,   30,
+ /*   580 */    88,   89,   90,   34,   35,  104,   37,   54,   39,   62,
+ /*   590 */    41,   88,   89,   90,   45,   38,   39,   38,   39,  105,
+ /*   600 */    51,  106,   64,  111,   56,   37,  103,   88,   89,   90,
+ /*   610 */    53,  109,   53,   88,   89,   90,   88,   89,   90,   88,
+ /*   620 */    89,   90,   88,   89,   90,   88,   89,   90,  113,   37,
+ /*   630 */    81,   70,  114,    2,    3,    4,    5,    6,    7,  115,
+ /*   640 */    88,   89,   90,   88,   89,   90,   88,   89,   90,   70,
+ /*   650 */    88,   89,   90,  114,   88,   89,   90,   88,   89,   90,
+ /*   660 */    29,   30,   88,   89,   90,   34,   35,  114,   37,  105,
+ /*   670 */    39,   40,   41,   88,   89,   90,   45,  114,   78,    2,
+ /*   680 */     3,    4,    5,    6,    7,   88,   89,   90,  117,   88,
+ /*   690 */    89,   90,   88,   89,   90,   88,   89,   90,   88,   89,
+ /*   700 */    90,   88,   89,   90,   89,   37,   29,   30,   88,   89,
+ /*   710 */    90,   34,   35,  114,   37,   91,   39,  105,   41,  102,
+ /*   720 */   112,  110,   45,   88,   89,   90,  100,  119,   51,  119,
+ /*   730 */   112,    2,    3,    4,    5,    6,    7,   88,   89,   90,
+ /*   740 */    88,   89,   90,   88,   89,   90,   88,   89,   90,   88,
+ /*   750 */    89,   90,   88,   89,   90,   88,   89,   90,   29,   30,
+ /*   760 */    88,   89,   90,   34,   35,  119,   37,  119,   39,  119,
+ /*   770 */    41,   88,   89,   90,   45,   88,   89,   90,   88,   89,
+ /*   780 */    90,   88,   89,   90,   88,   89,   90,
+};
+#define YY_SHIFT_USE_DFLT (-13)
+#define YY_SHIFT_COUNT (166)
+#define YY_SHIFT_MIN   (-12)
+#define YY_SHIFT_MAX   (729)
+static const short yy_shift_ofst[] = {
+ /*     0 */   364,  549,  677,  677,  498,  677,  677,  677,  729,  729,
+ /*    10 */   631,  729,  729,  729,  729,  729,  729,  729,  729,  729,
+ /*    20 */   729,  729,  729,  729,  729,  729,  729,  729,  729,  729,
+ /*    30 */   729,  729,  729,  729,  729,  729,  729,  729,  729,  729,
+ /*    40 */   729,  729,  729,  729,  729,  729,  729,  729,  214,  214,
+ /*    50 */   668,   -8,   -8,  477,  559,  360,  113,  184,  592,  668,
+ /*    60 */   600,  592,  538,  592,  592,  579,  592,  561,  568,  548,
+ /*    70 */   538,  533,  527,  253,  199,  170,  123,   86,   53,  300,
+ /*    80 */   300,  300,  300,  300,  300,  300,  300,  300,  300,  300,
+ /*    90 */   300,  300,  300,  300,  300,  327,   18,  438,  359,  461,
+ /*   100 */   217,  217,  217,  134,  134,   94,   24,  368,  323,  557,
+ /*   110 */   523,   43,  213,  436,  412,  404,  247,   84,  132,  -12,
+ /*   120 */   540,  491,  493,  485,  484,  496,  474,  475,  434,  443,
+ /*   130 */   429,  501,  497,  462,  449,  453,  239,  349,  435,  406,
+ /*   140 */   391,  390,  388,  387,  377,  374,  349,  373,  338,  321,
+ /*   150 */   278,  269,  239,  239,  216,  200,  122,  168,  168,  168,
+ /*   160 */   168,  168,  115,   91,  160,   87,  119,
+};
+#define YY_REDUCE_USE_DFLT (-51)
+#define YY_REDUCE_COUNT (72)
+#define YY_REDUCE_MIN   (-50)
+#define YY_REDUCE_MAX   (696)
+static const short yy_reduce_ofst[] = {
+ /*     0 */   324,  469,  419,  204,  433,  422,  337,  207,  480,  452,
+ /*    10 */    83,  503,  492,  696,  693,  690,  687,  683,  672,  667,
+ /*    20 */   664,  661,  658,  655,  652,  649,  635,  620,  613,  610,
+ /*    30 */   607,  604,  601,  597,  585,  574,  569,  566,  562,  558,
+ /*    40 */   555,  552,  537,  534,  531,  528,  525,  519,  147,  -50,
+ /*    50 */   218,  618,  608,  626,  611,  624,  612,  617,  599,  615,
+ /*    60 */   571,  563,  564,  553,  539,  524,  518,  515,  502,  495,
+ /*    70 */   494,  481,  473,
+};
+static const YYACTIONTYPE yy_default[] = {
+ /*     0 */   327,  333,  333,  333,  333,  333,  333,  333,  262,  262,
+ /*    10 */   333,  278,  333,  333,  333,  333,  333,  333,  333,  333,
+ /*    20 */   333,  333,  333,  333,  333,  333,  333,  333,  333,  333,
+ /*    30 */   333,  333,  333,  333,  333,  333,  333,  333,  333,  333,
+ /*    40 */   333,  333,  333,  333,  333,  333,  333,  333,  333,  333,
+ /*    50 */   333,  303,  303,  297,  289,  333,  307,  275,  333,  333,
+ /*    60 */   323,  333,  307,  333,  333,  318,  333,  313,  333,  294,
+ /*    70 */   307,  281,  304,  333,  279,  305,  273,  333,  333,  331,
+ /*    80 */   325,  321,  324,  322,  235,  236,  237,  308,  296,  264,
+ /*    90 */   263,  273,  306,  260,  238,  241,  240,  246,  245,  244,
+ /*   100 */   252,  251,  243,  250,  242,  247,  248,  333,  333,  333,
+ /*   110 */   286,  333,  330,  333,  333,  333,  333,  333,  333,  229,
+ /*   120 */   333,  333,  333,  333,  333,  333,  333,  333,  333,  333,
+ /*   130 */   333,  333,  333,  333,  333,  333,  269,  283,  333,  333,
+ /*   140 */   333,  333,  333,  333,  333,  333,  284,  282,  295,  261,
+ /*   150 */   333,  333,  270,  268,  298,  333,  333,  256,  255,  254,
+ /*   160 */   253,  249,  333,  333,  226,  333,  333,  332,  329,  328,
+ /*   170 */   326,  320,  226,  319,  317,  316,  314,  315,  312,  311,
+ /*   180 */   310,  309,  265,  239,  232,  234,  277,  276,  287,  285,
+ /*   190 */   291,  293,  292,  290,  288,  274,  280,  272,  271,  267,
+ /*   200 */   299,  302,  301,  300,  266,  257,  258,  259,  233,  231,
+ /*   210 */   230,  228,  227,  225,  224,  223,  222,  221,  220,
+};
+
+/* The next table maps tokens into fallback tokens.  If a construct
+** like the following:
+** 
+**      %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z.  Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack.  Information stored includes:
+**
+**   +  The state number for the parser at this level of the stack.
+**
+**   +  The value of the token stored at this level of the stack.
+**      (In other words, the "major" token.)
+**
+**   +  The semantic value stored at this level of the stack.  This is
+**      the information used by the action routines in the grammar.
+**      It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+  YYACTIONTYPE stateno;  /* The state-number */
+  YYCODETYPE major;      /* The major token value.  This is the code
+                         ** number for the token at this stack level */
+  YYMINORTYPE minor;     /* The user-supplied minor token value.  This
+                         ** is the value of the token  */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+  int yyidx;                    /* Index of top element in stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+  int yyidxMax;                 /* Maximum value of yyidx */
+#endif
+  int yyerrcnt;                 /* Shifts left before out of the error */
+  xjd1ParserARG_SDECL                /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+  int yystksz;                  /* Current side of the stack */
+  yyStackEntry *yystack;        /* The parser's stack */
+#else
+  yyStackEntry yystack[YYSTACKDEPTH];  /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* 
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message.  Tracing is turned off
+** by making either argument NULL 
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+**      If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+**      line of trace output.  If NULL, then tracing is
+**      turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void xjd1ParserTrace(FILE *TraceFILE, char *zTracePrompt){
+  yyTraceFILE = TraceFILE;
+  yyTracePrompt = zTracePrompt;
+  if( yyTraceFILE==0 ) yyTracePrompt = 0;
+  else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required.  The following table supplies these names */
+static const char *const yyTokenName[] = { 
+  "$",             "SEMI",          "INTEGER",       "FLOAT",       
+  "STRING",        "TRUE",          "FALSE",         "NULL",        
+  "QM",            "OR",            "AND",           "BITOR",       
+  "BITXOR",        "BITAND",        "ILIKEOP",       "LIKEOP",      
+  "NE",            "EQEQ",          "EQ3",           "NE3",         
+  "WITHIN",        "IN",            "GT",            "LE",          
+  "LT",            "GE",            "LSHIFT",        "RSHIFT",      
+  "URSHIFT",       "PLUS",          "MINUS",         "STAR",        
+  "SLASH",         "REM",           "BITNOT",        "BANG",        
+  "COLLATE",       "ID",            "DOT",           "LB",          
+  "RB",            "LC",            "RC",            "COLON",       
+  "COMMA",         "LP",            "RP",            "UNION",       
+  "EXCEPT",        "INTERSECT",     "ALL",           "SELECT",      
+  "DISTINCT",      "AS",            "FROM",          "FLATTENOP",   
+  "GROUP",         "BY",            "HAVING",        "ORDER",       
+  "ASCENDING",     "DESCENDING",    "LIMIT",         "OFFSET",      
+  "WHERE",         "BEGIN",         "ROLLBACK",      "COMMIT",      
+  "CREATE",        "COLLECTION",    "IF",            "NOT",         
+  "EXISTS",        "DROP",          "DELETE",        "UPDATE",      
+  "SET",           "EQ",            "ELSE",          "INSERT",      
+  "INTO",          "VALUE",         "ASYNC",         "SYNC",        
+  "PRAGMA",        "error",         "cmd",           "input",       
+  "jvalue",        "lvalue",        "expr",          "structlist",  
+  "arraylist",     "exprlist",      "select",        "nexprlist",   
+  "selectcore",    "compound",      "esel",          "x",           
+  "orderby_opt",   "limit_opt",     "dist_opt",      "sel_result",  
+  "from",          "where_opt",     "groupby_opt",   "fromlist",    
+  "fromitem",      "path",          "eachalias",     "sortlist",    
+  "sortorder",     "ifnotexists",   "tabname",       "ifexists",    
+  "setlist",       "upsert_opt",    "async",       
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+ /*   0 */ "input ::= cmd SEMI",
+ /*   1 */ "jvalue ::= INTEGER",
+ /*   2 */ "jvalue ::= FLOAT",
+ /*   3 */ "jvalue ::= STRING",
+ /*   4 */ "jvalue ::= TRUE",
+ /*   5 */ "jvalue ::= FALSE",
+ /*   6 */ "jvalue ::= NULL",
+ /*   7 */ "lvalue ::= ID",
+ /*   8 */ "lvalue ::= lvalue DOT ID",
+ /*   9 */ "lvalue ::= lvalue LB expr RB",
+ /*  10 */ "expr ::= lvalue",
+ /*  11 */ "expr ::= jvalue",
+ /*  12 */ "expr ::= LC structlist RC",
+ /*  13 */ "expr ::= LC RC",
+ /*  14 */ "expr ::= LB arraylist RB",
+ /*  15 */ "expr ::= LB RB",
+ /*  16 */ "structlist ::= ID|STRING COLON expr",
+ /*  17 */ "structlist ::= structlist COMMA ID|STRING COLON expr",
+ /*  18 */ "arraylist ::= expr",
+ /*  19 */ "arraylist ::= arraylist COMMA expr",
+ /*  20 */ "expr ::= ID LP exprlist RP",
+ /*  21 */ "expr ::= expr AND expr",
+ /*  22 */ "expr ::= expr OR expr",
+ /*  23 */ "expr ::= expr LT|GT|GE|LE expr",
+ /*  24 */ "expr ::= expr EQEQ|NE|EQ3|NE3 expr",
+ /*  25 */ "expr ::= expr BITAND expr",
+ /*  26 */ "expr ::= expr BITXOR expr",
+ /*  27 */ "expr ::= expr BITOR expr",
+ /*  28 */ "expr ::= expr LSHIFT|RSHIFT|URSHIFT expr",
+ /*  29 */ "expr ::= expr PLUS|MINUS expr",
+ /*  30 */ "expr ::= expr STAR|SLASH|REM expr",
+ /*  31 */ "expr ::= expr IN|WITHIN expr",
+ /*  32 */ "expr ::= expr ILIKEOP expr",
+ /*  33 */ "expr ::= expr LIKEOP expr",
+ /*  34 */ "expr ::= BANG expr",
+ /*  35 */ "expr ::= BITNOT expr",
+ /*  36 */ "expr ::= MINUS expr",
+ /*  37 */ "expr ::= PLUS expr",
+ /*  38 */ "expr ::= LP select RP",
+ /*  39 */ "expr ::= LP expr RP",
+ /*  40 */ "expr ::= expr COLLATE ID|STRING",
+ /*  41 */ "expr ::= expr QM expr COLON expr",
+ /*  42 */ "exprlist ::= nexprlist",
+ /*  43 */ "exprlist ::=",
+ /*  44 */ "nexprlist ::= expr",
+ /*  45 */ "nexprlist ::= nexprlist COMMA expr",
+ /*  46 */ "cmd ::= select",
+ /*  47 */ "select ::= compound orderby_opt limit_opt",
+ /*  48 */ "compound ::= selectcore",
+ /*  49 */ "compound ::= esel UNION esel",
+ /*  50 */ "compound ::= esel UNION ALL esel",
+ /*  51 */ "compound ::= esel EXCEPT esel",
+ /*  52 */ "compound ::= esel INTERSECT esel",
+ /*  53 */ "esel ::= compound",
+ /*  54 */ "esel ::= expr",
+ /*  55 */ "selectcore ::= SELECT dist_opt sel_result from where_opt groupby_opt",
+ /*  56 */ "dist_opt ::=",
+ /*  57 */ "dist_opt ::= DISTINCT",
+ /*  58 */ "dist_opt ::= ALL",
+ /*  59 */ "sel_result ::=",
+ /*  60 */ "sel_result ::= expr",
+ /*  61 */ "sel_result ::= expr AS ID",
+ /*  62 */ "from ::=",
+ /*  63 */ "from ::= FROM fromlist",
+ /*  64 */ "fromlist ::= fromitem",
+ /*  65 */ "fromlist ::= fromlist COMMA fromitem",
+ /*  66 */ "fromitem ::= LP select RP AS ID",
+ /*  67 */ "fromitem ::= ID",
+ /*  68 */ "fromitem ::= path AS ID",
+ /*  69 */ "fromitem ::= fromitem FLATTENOP LP path eachalias RP",
+ /*  70 */ "eachalias ::=",
+ /*  71 */ "eachalias ::= AS ID|STRING",
+ /*  72 */ "path ::= ID",
+ /*  73 */ "path ::= path DOT ID",
+ /*  74 */ "path ::= path LB ID|STRING RB",
+ /*  75 */ "groupby_opt ::=",
+ /*  76 */ "groupby_opt ::= GROUP BY exprlist",
+ /*  77 */ "groupby_opt ::= GROUP BY exprlist HAVING expr",
+ /*  78 */ "orderby_opt ::=",
+ /*  79 */ "orderby_opt ::= ORDER BY sortlist",
+ /*  80 */ "sortlist ::= sortlist COMMA expr sortorder",
+ /*  81 */ "sortlist ::= expr sortorder",
+ /*  82 */ "sortorder ::= ASCENDING",
+ /*  83 */ "sortorder ::= DESCENDING",
+ /*  84 */ "sortorder ::=",
+ /*  85 */ "limit_opt ::=",
+ /*  86 */ "limit_opt ::= LIMIT expr",
+ /*  87 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /*  88 */ "where_opt ::=",
+ /*  89 */ "where_opt ::= WHERE expr",
+ /*  90 */ "cmd ::= BEGIN ID",
+ /*  91 */ "cmd ::= ROLLBACK ID",
+ /*  92 */ "cmd ::= COMMIT ID",
+ /*  93 */ "cmd ::= CREATE COLLECTION ifnotexists tabname",
+ /*  94 */ "ifnotexists ::=",
+ /*  95 */ "ifnotexists ::= IF NOT EXISTS",
+ /*  96 */ "tabname ::= ID",
+ /*  97 */ "cmd ::= DROP COLLECTION ifexists tabname",
+ /*  98 */ "ifexists ::= IF EXISTS",
+ /*  99 */ "ifexists ::=",
+ /* 100 */ "cmd ::= DELETE FROM tabname where_opt",
+ /* 101 */ "cmd ::= UPDATE tabname SET setlist where_opt upsert_opt",
+ /* 102 */ "setlist ::= setlist COMMA lvalue EQ expr",
+ /* 103 */ "setlist ::= lvalue EQ expr",
+ /* 104 */ "upsert_opt ::=",
+ /* 105 */ "upsert_opt ::= ELSE INSERT expr",
+ /* 106 */ "cmd ::= async INSERT INTO tabname VALUE expr",
+ /* 107 */ "cmd ::= async INSERT INTO tabname select",
+ /* 108 */ "async ::=",
+ /* 109 */ "async ::= ASYNC",
+ /* 110 */ "async ::= SYNC",
+ /* 111 */ "cmd ::= PRAGMA ID",
+ /* 112 */ "cmd ::= PRAGMA ID EQ expr",
+ /* 113 */ "cmd ::= PRAGMA ID LP expr RP",
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void yyGrowStack(yyParser *p){
+  int newSize;
+  yyStackEntry *pNew;
+
+  newSize = p->yystksz*2 + 100;
+  pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+  if( pNew ){
+    p->yystack = pNew;
+    p->yystksz = newSize;
+#ifndef NDEBUG
+    if( yyTraceFILE ){
+      fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
+              yyTracePrompt, p->yystksz);
+    }
+#endif
+  }
+}
+#endif
+
+/* 
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser.  This pointer is used in subsequent calls
+** to xjd1Parser and xjd1ParserFree.
+*/
+void *xjd1ParserAlloc(void *(*mallocProc)(size_t)){
+  yyParser *pParser;
+  pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+  if( pParser ){
+    pParser->yyidx = -1;
+#ifdef YYTRACKMAXSTACKDEPTH
+    pParser->yyidxMax = 0;
+#endif
+#if YYSTACKDEPTH<=0
+    pParser->yystack = NULL;
+    pParser->yystksz = 0;
+    yyGrowStack(pParser);
+#endif
+  }
+  return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol.  The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(
+  yyParser *yypParser,    /* The parser */
+  YYCODETYPE yymajor,     /* Type code for object to destroy */
+  YYMINORTYPE *yypminor   /* The object to be destroyed */
+){
+  xjd1ParserARG_FETCH;
+  switch( yymajor ){
+    /* Here is inserted the actions which take place when a
+    ** terminal or non-terminal is destroyed.  This can happen
+    ** when the symbol is popped from the stack during a
+    ** reduce or during error processing or when a parser is 
+    ** being destroyed before it is finished parsing.
+    **
+    ** Note: during a reduce, the only symbols destroyed are those
+    ** which appear on the RHS of the rule, but which are not used
+    ** inside the C code.
+    */
+    case 86: /* cmd */
+{
+#line 64 "parse.y"
+(void)p;
+#line 1040 "parse.c"
+}
+      break;
+    default:  break;   /* If no destructor action specified: do nothing */
+  }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+  YYCODETYPE yymajor;
+  yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+  if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+  if( yyTraceFILE && pParser->yyidx>=0 ){
+    fprintf(yyTraceFILE,"%sPopping %s\n",
+      yyTracePrompt,
+      yyTokenName[yytos->major]);
+  }
+#endif
+  yymajor = yytos->major;
+  yy_destructor(pParser, yymajor, &yytos->minor);
+  pParser->yyidx--;
+  return yymajor;
+}
+
+/* 
+** Deallocate and destroy a parser.  Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li>  A pointer to the parser.  This should be a pointer
+**       obtained from xjd1ParserAlloc.
+** <li>  A pointer to a function used to reclaim memory obtained
+**       from malloc.
+** </ul>
+*/
+void xjd1ParserFree(
+  void *p,                    /* The parser to be deleted */
+  void (*freeProc)(void*)     /* Function used to reclaim memory */
+){
+  yyParser *pParser = (yyParser*)p;
+  if( pParser==0 ) return;
+  while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+  free(pParser->yystack);
+#endif
+  (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int xjd1ParserStackPeak(void *p){
+  yyParser *pParser = (yyParser*)p;
+  return pParser->yyidxMax;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+  yyParser *pParser,        /* The parser */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+  int stateno = pParser->yystack[pParser->yyidx].stateno;
+ 
+  if( stateno>YY_SHIFT_COUNT
+   || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
+    return yy_default[stateno];
+  }
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    if( iLookAhead>0 ){
+#ifdef YYFALLBACK
+      YYCODETYPE iFallback;            /* Fallback token */
+      if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+             && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+             yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+        }
+#endif
+        return yy_find_shift_action(pParser, iFallback);
+      }
+#endif
+#ifdef YYWILDCARD
+      {
+        int j = i - iLookAhead + YYWILDCARD;
+        if( 
+#if YY_SHIFT_MIN+YYWILDCARD<0
+          j>=0 &&
+#endif
+#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
+          j<YY_ACTTAB_COUNT &&
+#endif
+          yy_lookahead[j]==YYWILDCARD
+        ){
+#ifndef NDEBUG
+          if( yyTraceFILE ){
+            fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+               yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+          }
+#endif /* NDEBUG */
+          return yy_action[j];
+        }
+      }
+#endif /* YYWILDCARD */
+    }
+    return yy_default[stateno];
+  }else{
+    return yy_action[i];
+  }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead.  If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+  int stateno,              /* Current state number */
+  YYCODETYPE iLookAhead     /* The look-ahead token */
+){
+  int i;
+#ifdef YYERRORSYMBOL
+  if( stateno>YY_REDUCE_COUNT ){
+    return yy_default[stateno];
+  }
+#else
+  assert( stateno<=YY_REDUCE_COUNT );
+#endif
+  i = yy_reduce_ofst[stateno];
+  assert( i!=YY_REDUCE_USE_DFLT );
+  assert( iLookAhead!=YYNOCODE );
+  i += iLookAhead;
+#ifdef YYERRORSYMBOL
+  if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+    return yy_default[stateno];
+  }
+#else
+  assert( i>=0 && i<YY_ACTTAB_COUNT );
+  assert( yy_lookahead[i]==iLookAhead );
+#endif
+  return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+   xjd1ParserARG_FETCH;
+   yypParser->yyidx--;
+#ifndef NDEBUG
+   if( yyTraceFILE ){
+     fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+   }
+#endif
+   while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+   /* Here code is inserted which will execute if the parser
+   ** stack every overflows */
+   xjd1ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+  yyParser *yypParser,          /* The parser to be shifted */
+  int yyNewState,               /* The new state to shift in */
+  int yyMajor,                  /* The major token to shift in */
+  YYMINORTYPE *yypMinor         /* Pointer to the minor token to shift in */
+){
+  yyStackEntry *yytos;
+  yypParser->yyidx++;
+#ifdef YYTRACKMAXSTACKDEPTH
+  if( yypParser->yyidx>yypParser->yyidxMax ){
+    yypParser->yyidxMax = yypParser->yyidx;
+  }
+#endif
+#if YYSTACKDEPTH>0 
+  if( yypParser->yyidx>=YYSTACKDEPTH ){
+    yyStackOverflow(yypParser, yypMinor);
+    return;
+  }
+#else
+  if( yypParser->yyidx>=yypParser->yystksz ){
+    yyGrowStack(yypParser);
+    if( yypParser->yyidx>=yypParser->yystksz ){
+      yyStackOverflow(yypParser, yypMinor);
+      return;
+    }
+  }
+#endif
+  yytos = &yypParser->yystack[yypParser->yyidx];
+  yytos->stateno = (YYACTIONTYPE)yyNewState;
+  yytos->major = (YYCODETYPE)yyMajor;
+  yytos->minor = *yypMinor;
+#ifndef NDEBUG
+  if( yyTraceFILE && yypParser->yyidx>0 ){
+    int i;
+    fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+    fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+    for(i=1; i<=yypParser->yyidx; i++)
+      fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+    fprintf(yyTraceFILE,"\n");
+  }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+  YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
+  unsigned char nrhs;     /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+  { 87, 2 },
+  { 88, 1 },
+  { 88, 1 },
+  { 88, 1 },
+  { 88, 1 },
+  { 88, 1 },
+  { 88, 1 },
+  { 89, 1 },
+  { 89, 3 },
+  { 89, 4 },
+  { 90, 1 },
+  { 90, 1 },
+  { 90, 3 },
+  { 90, 2 },
+  { 90, 3 },
+  { 90, 2 },
+  { 91, 3 },
+  { 91, 5 },
+  { 92, 1 },
+  { 92, 3 },
+  { 90, 4 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 2 },
+  { 90, 2 },
+  { 90, 2 },
+  { 90, 2 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 3 },
+  { 90, 5 },
+  { 93, 1 },
+  { 93, 0 },
+  { 95, 1 },
+  { 95, 3 },
+  { 86, 1 },
+  { 94, 3 },
+  { 97, 1 },
+  { 97, 3 },
+  { 97, 4 },
+  { 97, 3 },
+  { 97, 3 },
+  { 98, 1 },
+  { 98, 1 },
+  { 96, 6 },
+  { 102, 0 },
+  { 102, 1 },
+  { 102, 1 },
+  { 103, 0 },
+  { 103, 1 },
+  { 103, 3 },
+  { 104, 0 },
+  { 104, 2 },
+  { 107, 1 },
+  { 107, 3 },
+  { 108, 5 },
+  { 108, 1 },
+  { 108, 3 },
+  { 108, 6 },
+  { 110, 0 },
+  { 110, 2 },
+  { 109, 1 },
+  { 109, 3 },
+  { 109, 4 },
+  { 106, 0 },
+  { 106, 3 },
+  { 106, 5 },
+  { 100, 0 },
+  { 100, 3 },
+  { 111, 4 },
+  { 111, 2 },
+  { 112, 1 },
+  { 112, 1 },
+  { 112, 0 },
+  { 101, 0 },
+  { 101, 2 },
+  { 101, 4 },
+  { 105, 0 },
+  { 105, 2 },
+  { 86, 2 },
+  { 86, 2 },
+  { 86, 2 },
+  { 86, 4 },
+  { 113, 0 },
+  { 113, 3 },
+  { 114, 1 },
+  { 86, 4 },
+  { 115, 2 },
+  { 115, 0 },
+  { 86, 4 },
+  { 86, 6 },
+  { 116, 5 },
+  { 116, 3 },
+  { 117, 0 },
+  { 117, 3 },
+  { 86, 6 },
+  { 86, 5 },
+  { 118, 0 },
+  { 118, 1 },
+  { 118, 1 },
+  { 86, 2 },
+  { 86, 4 },
+  { 86, 5 },
+};
+
+static void yy_accept(yyParser*);  /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+  yyParser *yypParser,         /* The parser */
+  int yyruleno                 /* Number of the rule by which to reduce */
+){
+  int yygoto;                     /* The next state */
+  int yyact;                      /* The next action */
+  YYMINORTYPE yygotominor;        /* The LHS of the rule reduced */
+  yyStackEntry *yymsp;            /* The top of the parser's stack */
+  int yysize;                     /* Amount to pop the stack */
+  xjd1ParserARG_FETCH;
+  yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+  if( yyTraceFILE && yyruleno>=0 
+        && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+    fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+      yyRuleName[yyruleno]);
+  }
+#endif /* NDEBUG */
+
+  /* Silence complaints from purify about yygotominor being uninitialized
+  ** in some cases when it is copied into the stack after the following
+  ** switch.  yygotominor is uninitialized when a rule reduces that does
+  ** not set the value of its left-hand side nonterminal.  Leaving the
+  ** value of the nonterminal uninitialized is utterly harmless as long
+  ** as the value is never used.  So really the only thing this code
+  ** accomplishes is to quieten purify.  
+  **
+  ** 2007-01-16:  The wireshark project (www.wireshark.org) reports that
+  ** without this code, their parser segfaults.  I'm not sure what there
+  ** parser is doing to make this happen.  This is the second bug report
+  ** from wireshark this week.  Clearly they are stressing Lemon in ways
+  ** that it has not been previously stressed...  (SQLite ticket #2172)
+  */
+  /*memset(&yygotominor, 0, sizeof(yygotominor));*/
+  yygotominor = yyzerominor;
+
+
+  switch( yyruleno ){
+  /* Beginning here are the reduction cases.  A typical example
+  ** follows:
+  **   case 0:
+  **  #line <lineno> <grammarfile>
+  **     { ... }           // User supplied code
+  **  #line <lineno> <thisfile>
+  **     break;
+  */
+      case 0: /* input ::= cmd SEMI */
+#line 65 "parse.y"
+{p->pCmd = yymsp[-1].minor.yy156;}
+#line 1449 "parse.c"
+        break;
+      case 1: /* jvalue ::= INTEGER */
+      case 2: /* jvalue ::= FLOAT */ yytestcase(yyruleno==2);
+#line 113 "parse.y"
+{yygotominor.yy181 = jsonReal(p,&yymsp[0].minor.yy0);}
+#line 1455 "parse.c"
+        break;
+      case 3: /* jvalue ::= STRING */
+#line 115 "parse.y"
+{yygotominor.yy181 = jsonString(p,&yymsp[0].minor.yy0);}
+#line 1460 "parse.c"
+        break;
+      case 4: /* jvalue ::= TRUE */
+#line 116 "parse.y"
+{yygotominor.yy181 = jsonType(p,XJD1_TRUE);}
+#line 1465 "parse.c"
+        break;
+      case 5: /* jvalue ::= FALSE */
+#line 117 "parse.y"
+{yygotominor.yy181 = jsonType(p,XJD1_FALSE);}
+#line 1470 "parse.c"
+        break;
+      case 6: /* jvalue ::= NULL */
+#line 118 "parse.y"
+{yygotominor.yy181 = jsonType(p,XJD1_NULL);}
+#line 1475 "parse.c"
+        break;
+      case 7: /* lvalue ::= ID */
+#line 278 "parse.y"
+{yygotominor.yy216 = idExpr(p,&yymsp[0].minor.yy0);}
+#line 1480 "parse.c"
+        break;
+      case 8: /* lvalue ::= lvalue DOT ID */
+#line 279 "parse.y"
+{yygotominor.yy216 = lvalueExpr(p,yymsp[-2].minor.yy216,&yymsp[0].minor.yy0);}
+#line 1485 "parse.c"
+        break;
+      case 9: /* lvalue ::= lvalue LB expr RB */
+#line 280 "parse.y"
+{yygotominor.yy216 = biExpr(p,yymsp[-3].minor.yy216,TK_LB,yymsp[-1].minor.yy216);}
+#line 1490 "parse.c"
+        break;
+      case 10: /* expr ::= lvalue */
+      case 89: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==89);
+      case 105: /* upsert_opt ::= ELSE INSERT expr */ yytestcase(yyruleno==105);
+#line 283 "parse.y"
+{yygotominor.yy216 = yymsp[0].minor.yy216;}
+#line 1497 "parse.c"
+        break;
+      case 11: /* expr ::= jvalue */
+#line 284 "parse.y"
+{yygotominor.yy216 = jsonExpr(p,yymsp[0].minor.yy181);}
+#line 1502 "parse.c"
+        break;
+      case 12: /* expr ::= LC structlist RC */
+#line 285 "parse.y"
+{yygotominor.yy216 = stExpr(p,yymsp[-1].minor.yy28);}
+#line 1507 "parse.c"
+        break;
+      case 13: /* expr ::= LC RC */
+#line 286 "parse.y"
+{yygotominor.yy216 = stExpr(p,0);}
+#line 1512 "parse.c"
+        break;
+      case 14: /* expr ::= LB arraylist RB */
+#line 287 "parse.y"
+{yygotominor.yy216 = arExpr(p,yymsp[-1].minor.yy28);}
+#line 1517 "parse.c"
+        break;
+      case 15: /* expr ::= LB RB */
+#line 288 "parse.y"
+{yygotominor.yy216 = arExpr(p,0);}
+#line 1522 "parse.c"
+        break;
+      case 16: /* structlist ::= ID|STRING COLON expr */
+#line 291 "parse.y"
+{yygotominor.yy28 = apndExpr(p,0,yymsp[0].minor.yy216,&yymsp[-2].minor.yy0);}
+#line 1527 "parse.c"
+        break;
+      case 17: /* structlist ::= structlist COMMA ID|STRING COLON expr */
+#line 293 "parse.y"
+{yygotominor.yy28 = apndExpr(p,yymsp[-4].minor.yy28,yymsp[0].minor.yy216,&yymsp[-2].minor.yy0);}
+#line 1532 "parse.c"
+        break;
+      case 18: /* arraylist ::= expr */
+      case 44: /* nexprlist ::= expr */ yytestcase(yyruleno==44);
+#line 295 "parse.y"
+{yygotominor.yy28 = apndExpr(p,0,yymsp[0].minor.yy216,0);}
+#line 1538 "parse.c"
+        break;
+      case 19: /* arraylist ::= arraylist COMMA expr */
+      case 45: /* nexprlist ::= nexprlist COMMA expr */ yytestcase(yyruleno==45);
+#line 296 "parse.y"
+{yygotominor.yy28 = apndExpr(p,yymsp[-2].minor.yy28,yymsp[0].minor.yy216,0);}
+#line 1544 "parse.c"
+        break;
+      case 20: /* expr ::= ID LP exprlist RP */
+#line 300 "parse.y"
+{yygotominor.yy216 = funcExpr(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy28);}
+#line 1549 "parse.c"
+        break;
+      case 21: /* expr ::= expr AND expr */
+      case 22: /* expr ::= expr OR expr */ yytestcase(yyruleno==22);
+      case 23: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==23);
+      case 24: /* expr ::= expr EQEQ|NE|EQ3|NE3 expr */ yytestcase(yyruleno==24);
+      case 25: /* expr ::= expr BITAND expr */ yytestcase(yyruleno==25);
+      case 26: /* expr ::= expr BITXOR expr */ yytestcase(yyruleno==26);
+      case 27: /* expr ::= expr BITOR expr */ yytestcase(yyruleno==27);
+      case 28: /* expr ::= expr LSHIFT|RSHIFT|URSHIFT expr */ yytestcase(yyruleno==28);
+      case 29: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==29);
+      case 30: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==30);
+      case 31: /* expr ::= expr IN|WITHIN expr */ yytestcase(yyruleno==31);
+      case 32: /* expr ::= expr ILIKEOP expr */ yytestcase(yyruleno==32);
+      case 33: /* expr ::= expr LIKEOP expr */ yytestcase(yyruleno==33);
+#line 301 "parse.y"
+{yygotominor.yy216 = biExpr(p,yymsp[-2].minor.yy216,yymsp[-1].major,yymsp[0].minor.yy216);}
+#line 1566 "parse.c"
+        break;
+      case 34: /* expr ::= BANG expr */
+      case 35: /* expr ::= BITNOT expr */ yytestcase(yyruleno==35);
+      case 36: /* expr ::= MINUS expr */ yytestcase(yyruleno==36);
+      case 37: /* expr ::= PLUS expr */ yytestcase(yyruleno==37);
+#line 315 "parse.y"
+{yygotominor.yy216 = biExpr(p,yymsp[0].minor.yy216,yymsp[-1].major,0);}
+#line 1574 "parse.c"
+        break;
+      case 38: /* expr ::= LP select RP */
+#line 319 "parse.y"
+{yygotominor.yy216 = subqExpr(p,yymsp[-1].minor.yy89);}
+#line 1579 "parse.c"
+        break;
+      case 39: /* expr ::= LP expr RP */
+#line 320 "parse.y"
+{yygotominor.yy216 = yymsp[-1].minor.yy216;}
+#line 1584 "parse.c"
+        break;
+      case 40: /* expr ::= expr COLLATE ID|STRING */
+#line 321 "parse.y"
+{yygotominor.yy216 = yymsp[-2].minor.yy216;}
+#line 1589 "parse.c"
+        break;
+      case 41: /* expr ::= expr QM expr COLON expr */
+#line 322 "parse.y"
+{yygotominor.yy216 = triExpr(p,yymsp[-4].minor.yy216,yymsp[-2].minor.yy216,yymsp[0].minor.yy216);}
+#line 1594 "parse.c"
+        break;
+      case 42: /* exprlist ::= nexprlist */
+      case 79: /* orderby_opt ::= ORDER BY sortlist */ yytestcase(yyruleno==79);
+#line 326 "parse.y"
+{yygotominor.yy28 = yymsp[0].minor.yy28;}
+#line 1600 "parse.c"
+        break;
+      case 43: /* exprlist ::= */
+      case 78: /* orderby_opt ::= */ yytestcase(yyruleno==78);
+#line 327 "parse.y"
+{yygotominor.yy28 = 0;}
+#line 1606 "parse.c"
+        break;
+      case 46: /* cmd ::= select */
+#line 333 "parse.y"
+{
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_SELECT;
+    pNew->u.q.pQuery = yymsp[0].minor.yy89;
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1618 "parse.c"
+        break;
+      case 47: /* select ::= compound orderby_opt limit_opt */
+#line 407 "parse.y"
+{
+  yymsp[-2].minor.yy89->pOrderBy = yymsp[-1].minor.yy28;
+  yymsp[-2].minor.yy89->pLimit = yymsp[0].minor.yy17.pLimit;
+  yymsp[-2].minor.yy89->pOffset = yymsp[0].minor.yy17.pOffset;
+  yygotominor.yy89 = yymsp[-2].minor.yy89;
+}
+#line 1628 "parse.c"
+        break;
+      case 48: /* compound ::= selectcore */
+      case 53: /* esel ::= compound */ yytestcase(yyruleno==53);
+#line 413 "parse.y"
+{yygotominor.yy89 = yymsp[0].minor.yy89;}
+#line 1634 "parse.c"
+        break;
+      case 49: /* compound ::= esel UNION esel */
+      case 51: /* compound ::= esel EXCEPT esel */ yytestcase(yyruleno==51);
+      case 52: /* compound ::= esel INTERSECT esel */ yytestcase(yyruleno==52);
+#line 414 "parse.y"
+{yygotominor.yy89=compoundQuery(p,yymsp[-2].minor.yy89,yymsp[-1].major,yymsp[0].minor.yy89);}
+#line 1641 "parse.c"
+        break;
+      case 50: /* compound ::= esel UNION ALL esel */
+#line 415 "parse.y"
+{yygotominor.yy89=compoundQuery(p,yymsp[-3].minor.yy89,yymsp[-1].major,yymsp[0].minor.yy89);}
+#line 1646 "parse.c"
+        break;
+      case 54: /* esel ::= expr */
+#line 419 "parse.y"
+{
+  /* TODO: Fix these error messages */
+  if( p->errCode==XJD1_OK ){
+    if( yymsp[0].minor.yy216->eClass!=XJD1_EXPR_Q ){
+      xjd1ParseError(p, XJD1_SYNTAX,
+        "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+      );
+    }else{
+      yygotominor.yy89 = yymsp[0].minor.yy216->u.subq.p;
+      if( yygotominor.yy89->pOrderBy || yygotominor.yy89->pLimit ){
+        xjd1ParseError(p, XJD1_SYNTAX,
+          "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+        );
+      }
+    }
+  }
+}
+#line 1667 "parse.c"
+        break;
+      case 55: /* selectcore ::= SELECT dist_opt sel_result from where_opt groupby_opt */
+#line 439 "parse.y"
+{yygotominor.yy89 = simpleQuery(p,yymsp[-4].minor.yy60,yymsp[-3].minor.yy90.pExpr,yymsp[-3].minor.yy90.zAs,yymsp[-2].minor.yy225,yymsp[-1].minor.yy216,&yymsp[0].minor.yy186);}
+#line 1672 "parse.c"
+        break;
+      case 56: /* dist_opt ::= */
+      case 58: /* dist_opt ::= ALL */ yytestcase(yyruleno==58);
+      case 94: /* ifnotexists ::= */ yytestcase(yyruleno==94);
+      case 99: /* ifexists ::= */ yytestcase(yyruleno==99);
+#line 443 "parse.y"
+{yygotominor.yy60 = 0;}
+#line 1680 "parse.c"
+        break;
+      case 57: /* dist_opt ::= DISTINCT */
+      case 95: /* ifnotexists ::= IF NOT EXISTS */ yytestcase(yyruleno==95);
+      case 98: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==98);
+#line 444 "parse.y"
+{yygotominor.yy60 = 1;}
+#line 1687 "parse.c"
+        break;
+      case 59: /* sel_result ::= */
+#line 452 "parse.y"
+{yygotominor.yy90.pExpr=0;yygotominor.yy90.zAs=0;}
+#line 1692 "parse.c"
+        break;
+      case 60: /* sel_result ::= expr */
+#line 453 "parse.y"
+{yygotominor.yy90.pExpr=yymsp[0].minor.yy216;yygotominor.yy90.zAs=0;}
+#line 1697 "parse.c"
+        break;
+      case 61: /* sel_result ::= expr AS ID */
+#line 454 "parse.y"
+{yygotominor.yy90.pExpr=yymsp[-2].minor.yy216;yygotominor.yy90.zAs=tokenStr(p, &yymsp[0].minor.yy0);}
+#line 1702 "parse.c"
+        break;
+      case 62: /* from ::= */
+#line 536 "parse.y"
+{yygotominor.yy225 = nullDataSrc(p);}
+#line 1707 "parse.c"
+        break;
+      case 63: /* from ::= FROM fromlist */
+      case 64: /* fromlist ::= fromitem */ yytestcase(yyruleno==64);
+#line 537 "parse.y"
+{yygotominor.yy225 = yymsp[0].minor.yy225;}
+#line 1713 "parse.c"
+        break;
+      case 65: /* fromlist ::= fromlist COMMA fromitem */
+#line 539 "parse.y"
+{yygotominor.yy225 = joinDataSrc(p,yymsp[-2].minor.yy225,yymsp[0].minor.yy225);}
+#line 1718 "parse.c"
+        break;
+      case 66: /* fromitem ::= LP select RP AS ID */
+#line 540 "parse.y"
+{yygotominor.yy225 = subqDataSrc(p,yymsp[-3].minor.yy89,&yymsp[0].minor.yy0);}
+#line 1723 "parse.c"
+        break;
+      case 67: /* fromitem ::= ID */
+#line 542 "parse.y"
+{yygotominor.yy225 = pathDataSrc(p,0,&yymsp[0].minor.yy0);}
+#line 1728 "parse.c"
+        break;
+      case 68: /* fromitem ::= path AS ID */
+#line 543 "parse.y"
+{yygotominor.yy225 = pathDataSrc(p,yymsp[-2].minor.yy216,&yymsp[0].minor.yy0);}
+#line 1733 "parse.c"
+        break;
+      case 69: /* fromitem ::= fromitem FLATTENOP LP path eachalias RP */
+#line 545 "parse.y"
+{
+  yygotominor.yy225 = flattenDataSrc(p,yymsp[-5].minor.yy225,&yymsp[-4].minor.yy0,yymsp[-2].minor.yy216,yymsp[-1].minor.yy216);
+}
+#line 1740 "parse.c"
+        break;
+      case 70: /* eachalias ::= */
+#line 550 "parse.y"
+{yygotominor.yy216=0;}
+#line 1745 "parse.c"
+        break;
+      case 71: /* eachalias ::= AS ID|STRING */
+#line 551 "parse.y"
+{yygotominor.yy216=idExpr(p,&yymsp[0].minor.yy0);}
+#line 1750 "parse.c"
+        break;
+      case 72: /* path ::= ID */
+#line 554 "parse.y"
+{yygotominor.yy216 = idExpr(p, &yymsp[0].minor.yy0);        }
+#line 1755 "parse.c"
+        break;
+      case 73: /* path ::= path DOT ID */
+#line 555 "parse.y"
+{yygotominor.yy216 = lvalueExpr(p, yymsp[-2].minor.yy216, &yymsp[0].minor.yy0); }
+#line 1760 "parse.c"
+        break;
+      case 74: /* path ::= path LB ID|STRING RB */
+#line 556 "parse.y"
+{yygotominor.yy216 = lvalueExpr(p, yymsp[-3].minor.yy216, &yymsp[-1].minor.yy0); }
+#line 1765 "parse.c"
+        break;
+      case 75: /* groupby_opt ::= */
+#line 559 "parse.y"
+{yygotominor.yy186.pGroupBy=0; yygotominor.yy186.pHaving=0;}
+#line 1770 "parse.c"
+        break;
+      case 76: /* groupby_opt ::= GROUP BY exprlist */
+#line 560 "parse.y"
+{yygotominor.yy186.pGroupBy=yymsp[0].minor.yy28; yygotominor.yy186.pHaving=0;}
+#line 1775 "parse.c"
+        break;
+      case 77: /* groupby_opt ::= GROUP BY exprlist HAVING expr */
+#line 562 "parse.y"
+{yygotominor.yy186.pGroupBy=yymsp[-2].minor.yy28; yygotominor.yy186.pHaving=yymsp[0].minor.yy216;}
+#line 1780 "parse.c"
+        break;
+      case 80: /* sortlist ::= sortlist COMMA expr sortorder */
+#line 569 "parse.y"
+{yygotominor.yy28 = apndExpr(p,yymsp[-3].minor.yy28,yymsp[-1].minor.yy216,yymsp[0].minor.yy0.n?&yymsp[0].minor.yy0:0);}
+#line 1785 "parse.c"
+        break;
+      case 81: /* sortlist ::= expr sortorder */
+#line 570 "parse.y"
+{yygotominor.yy28 = apndExpr(p,0,yymsp[-1].minor.yy216,yymsp[0].minor.yy0.n?&yymsp[0].minor.yy0:0);}
+#line 1790 "parse.c"
+        break;
+      case 82: /* sortorder ::= ASCENDING */
+      case 83: /* sortorder ::= DESCENDING */ yytestcase(yyruleno==83);
+      case 96: /* tabname ::= ID */ yytestcase(yyruleno==96);
+#line 571 "parse.y"
+{yygotominor.yy0 = yymsp[0].minor.yy0;}
+#line 1797 "parse.c"
+        break;
+      case 84: /* sortorder ::= */
+#line 573 "parse.y"
+{yygotominor.yy0.z=""; yygotominor.yy0.n=0;}
+#line 1802 "parse.c"
+        break;
+      case 85: /* limit_opt ::= */
+#line 576 "parse.y"
+{yygotominor.yy17.pLimit=0; yygotominor.yy17.pOffset=0;}
+#line 1807 "parse.c"
+        break;
+      case 86: /* limit_opt ::= LIMIT expr */
+#line 577 "parse.y"
+{yygotominor.yy17.pLimit=yymsp[0].minor.yy216; yygotominor.yy17.pOffset=0;}
+#line 1812 "parse.c"
+        break;
+      case 87: /* limit_opt ::= LIMIT expr OFFSET expr */
+#line 578 "parse.y"
+{yygotominor.yy17.pLimit=yymsp[-2].minor.yy216; yygotominor.yy17.pOffset=yymsp[0].minor.yy216;}
+#line 1817 "parse.c"
+        break;
+      case 88: /* where_opt ::= */
+      case 104: /* upsert_opt ::= */ yytestcase(yyruleno==104);
+#line 581 "parse.y"
+{yygotominor.yy216 = 0;}
+#line 1823 "parse.c"
+        break;
+      case 93: /* cmd ::= CREATE COLLECTION ifnotexists tabname */
+#line 592 "parse.y"
+{
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_CREATECOLLECTION;
+    pNew->u.crtab.ifExists = yymsp[-1].minor.yy60;
+    pNew->u.crtab.zName = tokenStr(p, &yymsp[0].minor.yy0);
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1836 "parse.c"
+        break;
+      case 97: /* cmd ::= DROP COLLECTION ifexists tabname */
+#line 608 "parse.y"
+{
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_DROPCOLLECTION;
+    pNew->u.crtab.ifExists = yymsp[-1].minor.yy60;
+    pNew->u.crtab.zName = tokenStr(p, &yymsp[0].minor.yy0);
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1849 "parse.c"
+        break;
+      case 100: /* cmd ::= DELETE FROM tabname where_opt */
+#line 624 "parse.y"
+{
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_DELETE;
+    pNew->u.del.zName = tokenStr(p, &yymsp[-1].minor.yy0);
+    pNew->u.del.pWhere = yymsp[0].minor.yy216;
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1862 "parse.c"
+        break;
+      case 101: /* cmd ::= UPDATE tabname SET setlist where_opt upsert_opt */
+#line 636 "parse.y"
+{
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_UPDATE;
+    pNew->u.update.zName = tokenStr(p, &yymsp[-4].minor.yy0);
+    pNew->u.update.pWhere = yymsp[-1].minor.yy216;
+    pNew->u.update.pChng = yymsp[-2].minor.yy28;
+    pNew->u.update.pUpsert = yymsp[0].minor.yy216;
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1877 "parse.c"
+        break;
+      case 102: /* setlist ::= setlist COMMA lvalue EQ expr */
+#line 649 "parse.y"
+{
+   yygotominor.yy28 = apndExpr(p,yymsp[-4].minor.yy28,yymsp[-2].minor.yy216,0);
+   yygotominor.yy28 = apndExpr(p,yygotominor.yy28,yymsp[0].minor.yy216,0);
+}
+#line 1885 "parse.c"
+        break;
+      case 103: /* setlist ::= lvalue EQ expr */
+#line 653 "parse.y"
+{
+   yygotominor.yy28 = apndExpr(p,0,yymsp[-2].minor.yy216,0);
+   yygotominor.yy28 = apndExpr(p,yygotominor.yy28,yymsp[0].minor.yy216,0);
+}
+#line 1893 "parse.c"
+        break;
+      case 106: /* cmd ::= async INSERT INTO tabname VALUE expr */
+#line 666 "parse.y"
+{
+  Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_INSERT;
+    pNew->u.ins.zName = tokenStr(p, &yymsp[-2].minor.yy0);
+    pNew->u.ins.pValue = yymsp[0].minor.yy216;
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1906 "parse.c"
+        break;
+      case 107: /* cmd ::= async INSERT INTO tabname select */
+#line 675 "parse.y"
+{
+  Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_INSERT;
+    pNew->u.ins.zName = tokenStr(p, &yymsp[-1].minor.yy0);
+    pNew->u.ins.pQuery = yymsp[0].minor.yy89;
+  }
+  yygotominor.yy156 = pNew;
+}
+#line 1919 "parse.c"
+        break;
+      case 111: /* cmd ::= PRAGMA ID */
+#line 701 "parse.y"
+{yygotominor.yy156 = makePrag(p,&yymsp[0].minor.yy0,0);}
+#line 1924 "parse.c"
+        break;
+      case 112: /* cmd ::= PRAGMA ID EQ expr */
+#line 702 "parse.y"
+{yygotominor.yy156 = makePrag(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy216);}
+#line 1929 "parse.c"
+        break;
+      case 113: /* cmd ::= PRAGMA ID LP expr RP */
+#line 703 "parse.y"
+{yygotominor.yy156 = makePrag(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy216);}
+#line 1934 "parse.c"
+        break;
+      default:
+      /* (90) cmd ::= BEGIN ID */ yytestcase(yyruleno==90);
+      /* (91) cmd ::= ROLLBACK ID */ yytestcase(yyruleno==91);
+      /* (92) cmd ::= COMMIT ID */ yytestcase(yyruleno==92);
+      /* (108) async ::= */ yytestcase(yyruleno==108);
+      /* (109) async ::= ASYNC */ yytestcase(yyruleno==109);
+      /* (110) async ::= SYNC */ yytestcase(yyruleno==110);
+        break;
+  };
+  yygoto = yyRuleInfo[yyruleno].lhs;
+  yysize = yyRuleInfo[yyruleno].nrhs;
+  yypParser->yyidx -= yysize;
+  yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
+  if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+    /* If we are not debugging and the reduce action popped at least
+    ** one element off the stack, then we can push the new element back
+    ** onto the stack here, and skip the stack overflow test in yy_shift().
+    ** That gives a significant speed improvement. */
+    if( yysize ){
+      yypParser->yyidx++;
+      yymsp -= yysize-1;
+      yymsp->stateno = (YYACTIONTYPE)yyact;
+      yymsp->major = (YYCODETYPE)yygoto;
+      yymsp->minor = yygotominor;
+    }else
+#endif
+    {
+      yy_shift(yypParser,yyact,yygoto,&yygotominor);
+    }
+  }else{
+    assert( yyact == YYNSTATE + YYNRULE + 1 );
+    yy_accept(yypParser);
+  }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef YYNOERRORRECOVERY
+static void yy_parse_failed(
+  yyParser *yypParser           /* The parser */
+){
+  xjd1ParserARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+  }
+#endif
+  while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+  /* Here code is inserted which will be executed whenever the
+  ** parser fails */
+  xjd1ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+  yyParser *yypParser,           /* The parser */
+  int yymajor,                   /* The major type of the error token */
+  YYMINORTYPE yyminor            /* The minor type of the error token */
+){
+  xjd1ParserARG_FETCH;
+#define TOKEN (yyminor.yy0)
+#line 37 "parse.y"
+
+  xjd1ParseError(p, XJD1_SYNTAX,
+      "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+  );
+#line 2007 "parse.c"
+  xjd1ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+  yyParser *yypParser           /* The parser */
+){
+  xjd1ParserARG_FETCH;
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+  }
+#endif
+  while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+  /* Here code is inserted which will be executed whenever the
+  ** parser accepts */
+  xjd1ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "xjd1ParserAlloc" which describes the current state of the parser.
+** The second argument is the major token number.  The third is
+** the minor token.  The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void xjd1Parser(
+  void *yyp,                   /* The parser */
+  int yymajor,                 /* The major token code number */
+  xjd1ParserTOKENTYPE yyminor       /* The value for the token */
+  xjd1ParserARG_PDECL               /* Optional %extra_argument parameter */
+){
+  YYMINORTYPE yyminorunion;
+  int yyact;            /* The parser action. */
+  int yyendofinput;     /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
+  int yyerrorhit = 0;   /* True if yymajor has invoked an error */
+#endif
+  yyParser *yypParser;  /* The parser */
+
+  /* (re)initialize the parser, if necessary */
+  yypParser = (yyParser*)yyp;
+  if( yypParser->yyidx<0 ){
+#if YYSTACKDEPTH<=0
+    if( yypParser->yystksz <=0 ){
+      /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
+      yyminorunion = yyzerominor;
+      yyStackOverflow(yypParser, &yyminorunion);
+      return;
+    }
+#endif
+    yypParser->yyidx = 0;
+    yypParser->yyerrcnt = -1;
+    yypParser->yystack[0].stateno = 0;
+    yypParser->yystack[0].major = 0;
+  }
+  yyminorunion.yy0 = yyminor;
+  yyendofinput = (yymajor==0);
+  xjd1ParserARG_STORE;
+
+#ifndef NDEBUG
+  if( yyTraceFILE ){
+    fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+  }
+#endif
+
+  do{
+    yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
+    if( yyact<YYNSTATE ){
+      assert( !yyendofinput );  /* Impossible to shift the $ token */
+      yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+      yypParser->yyerrcnt--;
+      yymajor = YYNOCODE;
+    }else if( yyact < YYNSTATE + YYNRULE ){
+      yy_reduce(yypParser,yyact-YYNSTATE);
+    }else{
+      assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
+      int yymx;
+#endif
+#ifndef NDEBUG
+      if( yyTraceFILE ){
+        fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+      }
+#endif
+#ifdef YYERRORSYMBOL
+      /* A syntax error has occurred.
+      ** The response to an error depends upon whether or not the
+      ** grammar defines an error token "ERROR".  
+      **
+      ** This is what we do if the grammar does define ERROR:
+      **
+      **  * Call the %syntax_error function.
+      **
+      **  * Begin popping the stack until we enter a state where
+      **    it is legal to shift the error symbol, then shift
+      **    the error symbol.
+      **
+      **  * Set the error count to three.
+      **
+      **  * Begin accepting and shifting new tokens.  No new error
+      **    processing will occur until three tokens have been
+      **    shifted successfully.
+      **
+      */
+      if( yypParser->yyerrcnt<0 ){
+        yy_syntax_error(yypParser,yymajor,yyminorunion);
+      }
+      yymx = yypParser->yystack[yypParser->yyidx].major;
+      if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+        if( yyTraceFILE ){
+          fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+             yyTracePrompt,yyTokenName[yymajor]);
+        }
+#endif
+        yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
+        yymajor = YYNOCODE;
+      }else{
+         while(
+          yypParser->yyidx >= 0 &&
+          yymx != YYERRORSYMBOL &&
+          (yyact = yy_find_reduce_action(
+                        yypParser->yystack[yypParser->yyidx].stateno,
+                        YYERRORSYMBOL)) >= YYNSTATE
+        ){
+          yy_pop_parser_stack(yypParser);
+        }
+        if( yypParser->yyidx < 0 || yymajor==0 ){
+          yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+          yy_parse_failed(yypParser);
+          yymajor = YYNOCODE;
+        }else if( yymx!=YYERRORSYMBOL ){
+          YYMINORTYPE u2;
+          u2.YYERRSYMDT = 0;
+          yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+        }
+      }
+      yypParser->yyerrcnt = 3;
+      yyerrorhit = 1;
+#elif defined(YYNOERRORRECOVERY)
+      /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
+      ** do any kind of error recovery.  Instead, simply invoke the syntax
+      ** error routine and continue going as if nothing had happened.
+      **
+      ** Applications can set this macro (for example inside %include) if
+      ** they intend to abandon the parse upon the first syntax error seen.
+      */
+      yy_syntax_error(yypParser,yymajor,yyminorunion);
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      yymajor = YYNOCODE;
+      
+#else  /* YYERRORSYMBOL is not defined */
+      /* This is what we do if the grammar does not define ERROR:
+      **
+      **  * Report an error message, and throw away the input token.
+      **
+      **  * If the input token is $, then fail the parse.
+      **
+      ** As before, subsequent error messages are suppressed until
+      ** three input tokens have been successfully shifted.
+      */
+      if( yypParser->yyerrcnt<=0 ){
+        yy_syntax_error(yypParser,yymajor,yyminorunion);
+      }
+      yypParser->yyerrcnt = 3;
+      yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+      if( yyendofinput ){
+        yy_parse_failed(yypParser);
+      }
+      yymajor = YYNOCODE;
+#endif
+    }
+  }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+  return;
+}

+ 84 - 0
unql/src/parse.h

@@ -0,0 +1,84 @@
+#define TK_SEMI                             1
+#define TK_INTEGER                          2
+#define TK_FLOAT                            3
+#define TK_STRING                           4
+#define TK_TRUE                             5
+#define TK_FALSE                            6
+#define TK_NULL                             7
+#define TK_QM                               8
+#define TK_OR                               9
+#define TK_AND                             10
+#define TK_BITOR                           11
+#define TK_BITXOR                          12
+#define TK_BITAND                          13
+#define TK_ILIKEOP                         14
+#define TK_LIKEOP                          15
+#define TK_NE                              16
+#define TK_EQEQ                            17
+#define TK_EQ3                             18
+#define TK_NE3                             19
+#define TK_WITHIN                          20
+#define TK_IN                              21
+#define TK_GT                              22
+#define TK_LE                              23
+#define TK_LT                              24
+#define TK_GE                              25
+#define TK_LSHIFT                          26
+#define TK_RSHIFT                          27
+#define TK_URSHIFT                         28
+#define TK_PLUS                            29
+#define TK_MINUS                           30
+#define TK_STAR                            31
+#define TK_SLASH                           32
+#define TK_REM                             33
+#define TK_BITNOT                          34
+#define TK_BANG                            35
+#define TK_COLLATE                         36
+#define TK_ID                              37
+#define TK_DOT                             38
+#define TK_LB                              39
+#define TK_RB                              40
+#define TK_LC                              41
+#define TK_RC                              42
+#define TK_COLON                           43
+#define TK_COMMA                           44
+#define TK_LP                              45
+#define TK_RP                              46
+#define TK_UNION                           47
+#define TK_EXCEPT                          48
+#define TK_INTERSECT                       49
+#define TK_ALL                             50
+#define TK_SELECT                          51
+#define TK_DISTINCT                        52
+#define TK_AS                              53
+#define TK_FROM                            54
+#define TK_FLATTENOP                       55
+#define TK_GROUP                           56
+#define TK_BY                              57
+#define TK_HAVING                          58
+#define TK_ORDER                           59
+#define TK_ASCENDING                       60
+#define TK_DESCENDING                      61
+#define TK_LIMIT                           62
+#define TK_OFFSET                          63
+#define TK_WHERE                           64
+#define TK_BEGIN                           65
+#define TK_ROLLBACK                        66
+#define TK_COMMIT                          67
+#define TK_CREATE                          68
+#define TK_COLLECTION                      69
+#define TK_IF                              70
+#define TK_NOT                             71
+#define TK_EXISTS                          72
+#define TK_DROP                            73
+#define TK_DELETE                          74
+#define TK_UPDATE                          75
+#define TK_SET                             76
+#define TK_EQ                              77
+#define TK_ELSE                            78
+#define TK_INSERT                          79
+#define TK_INTO                            80
+#define TK_VALUE                           81
+#define TK_ASYNC                           82
+#define TK_SYNC                            83
+#define TK_PRAGMA                          84

+ 703 - 0
unql/src/parse.y

@@ -0,0 +1,703 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*****************************************************************************
+** Language grammar
+*/
+
+// All token codes are small integers with #defines that begin with "TK_"
+%token_prefix TK_
+
+// The type of the data attached to each token is Token.  This is also the
+// default type for non-terminals.
+//
+%token_type {Token}
+%default_type {Token}
+
+// The generated parser function takes a 4th argument as follows:
+%extra_argument {Parse *p}
+
+// The name of the generated procedure that implements the parser
+// is as follows:
+%name xjd1Parser
+
+// Handle syntax errors.
+%syntax_error {
+  xjd1ParseError(p, XJD1_SYNTAX,
+      "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+  );
+}
+
+// The following text is included near the beginning of the C source
+// code file that implements the parser.
+//
+%include {
+#include "xjd1Int.h"
+
+/*
+** Disable all error recovery processing in the parser push-down
+** automaton.
+*/
+#define YYNOERRORRECOVERY 1
+
+/*
+** Make yytestcase() be a no-op
+*/
+#define yytestcase(X)
+
+} // end %include
+
+// Input is a single XJD1 command
+%type cmd {Command*}
+%destructor cmd {(void)p;}
+input ::= cmd(X) SEMI.   {p->pCmd = X;}
+
+/////////////////////////// Expression Processing /////////////////////////////
+//
+
+%include {
+  /* A JSON literal for a real number */
+  static JsonNode *jsonReal(Parse *p, Token *pTok){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = XJD1_REAL;
+      pNew->u.r = atof(pTok->z);
+    }
+    return pNew;
+  }
+
+  /* Convert a token into a zero-terminated string */
+  static char *tokenStr(Parse *p, Token *pTok){
+    char *z;
+    if( pTok ){
+      z = xjd1PoolDup(p->pPool, pTok->z, pTok->n);
+      if( z && z[0]=='"' ) xjd1DequoteString(z, pTok->n);
+    }else{
+      z = 0;
+    }
+    return z;
+  }
+
+  /* A JSON literal for a string */
+  static JsonNode *jsonString(Parse *p, Token *pTok){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = XJD1_STRING;
+      pNew->u.z = tokenStr(p, pTok);
+    }
+    return pNew;
+  }
+
+  /* A JSON literal for a boolean or NULL */
+  static JsonNode *jsonType(Parse *p, int eJType){
+    JsonNode *pNew = xjd1JsonNew(p->pPool);
+    if( pNew ){
+      pNew->eJType = eJType;
+    }
+    return pNew;
+  }
+}
+%type jvalue {JsonNode*}
+jvalue(A) ::= INTEGER(X).              {A = jsonReal(p,&X);}
+jvalue(A) ::= FLOAT(X).                {A = jsonReal(p,&X);}
+jvalue(A) ::= STRING(X).               {A = jsonString(p,&X);}
+jvalue(A) ::= TRUE.                    {A = jsonType(p,XJD1_TRUE);}
+jvalue(A) ::= FALSE.                   {A = jsonType(p,XJD1_FALSE);}
+jvalue(A) ::= NULL.                    {A = jsonType(p,XJD1_NULL);}
+
+%right QM.
+%left OR.
+%left AND.
+%left BITOR.
+%left BITXOR.
+%left BITAND.
+%left ILIKEOP LIKEOP NE EQEQ EQ3 NE3.
+%left WITHIN IN GT LE LT GE.
+%left LSHIFT RSHIFT URSHIFT.
+%left PLUS MINUS.
+%left STAR SLASH REM.
+%right BITNOT BANG.
+%left COLLATE.
+
+%include {
+  /* Generate an Expr object from an identifer token */
+  static Expr *idExpr(Parse *p, Token *pTok){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_ID;
+      pNew->eClass = XJD1_EXPR_TK;
+      pNew->u.id.zId = tokenStr(p, pTok);
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a binary operator on two
+  ** other Expr objects. */
+  static Expr *biExpr(Parse *p, Expr *pLeft, int eOp, Expr *pRight){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = eOp; 
+      pNew->eClass = XJD1_EXPR_BI;
+      pNew->u.bi.pLeft = pLeft;
+      pNew->u.bi.pRight = pRight;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a tertiary operator */
+  static Expr *triExpr(Parse *p, Expr *pTest, Expr *pTrue, Expr *pFalse){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_QM; 
+      pNew->eClass = XJD1_EXPR_TRI;
+      pNew->u.tri.pTest = pTest;
+      pNew->u.tri.pIfTrue = pTrue;
+      pNew->u.tri.pIfFalse = pFalse;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object for an lvalue */
+  static Expr *lvalueExpr(Parse *p, Expr *pLeft, Token *pId){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_DOT; 
+      pNew->eClass = XJD1_EXPR_LVALUE;
+      pNew->u.lvalue.pLeft = pLeft;
+      pNew->u.lvalue.zId = tokenStr(p, pId);
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a function call. */
+  static Expr *funcExpr(Parse *p, Token *pFName, ExprList *pArgs){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_FUNCTION; 
+      pNew->eClass = XJD1_EXPR_FUNC;
+      pNew->u.func.zFName = tokenStr(p, pFName);
+      if( pArgs==0 ){
+        pArgs = xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+      pNew->u.func.args = pArgs;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a subquery. */
+  static Expr *subqExpr(Parse *p, Query *pQuery){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_SELECT;
+      pNew->eClass = XJD1_EXPR_Q;
+      pNew->u.subq.p = pQuery;
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is a structure */
+  static Expr *stExpr(Parse *p, ExprList *pList){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_STRUCT;
+      pNew->eClass = XJD1_EXPR_STRUCT;
+      if( pList ){
+        pNew->u.st = pList;
+      }else{
+        pNew->u.st = (ExprList *)xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is an array7 */
+  static Expr *arExpr(Parse *p, ExprList *pList){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_ARRAY;
+      pNew->eClass = XJD1_EXPR_ARRAY;
+      if( pList ){
+        pNew->u.ar = pList;
+      }else{
+        pNew->u.ar = (ExprList *)xjd1PoolMallocZero(p->pPool, sizeof(ExprList));
+      }
+    }
+    return pNew;
+  }
+
+  /* Generate an Expr object that is JSON value literal */
+  static Expr *jsonExpr(Parse *p, JsonNode *pNode){
+    Expr *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eType = TK_JVALUE;
+      pNew->eClass = XJD1_EXPR_JSON;
+      pNew->u.json.p = pNode;
+    }
+    return pNew;
+  }
+
+  /* Append a new expression to an expression list.  Allocate the
+  ** expression list object if necessary. */
+  static ExprList *apndExpr(Parse *p, ExprList *pList, Expr *pExpr, Token *pT){
+    ExprItem *pItem;
+    if( pList==0 ){
+      pList = xjd1PoolMallocZero(p->pPool, sizeof(*pList)+4*sizeof(ExprItem));
+      if( pList==0 ) return 0;
+      pList->nEAlloc = 0;
+    }
+    if( pList->nEAlloc<=pList->nEItem ){
+      ExprItem *pNew;
+      int n = pList->nEAlloc*4;
+      if( n==0 ) n = 10;
+      pNew = xjd1PoolMalloc(p->pPool, sizeof(ExprItem)*n);
+      if( pNew==0 ) return pList;
+      memcpy(pNew, pList->apEItem, pList->nEItem*sizeof(ExprItem));
+      pList->nEAlloc = n;
+      pList->apEItem = pNew;
+    }
+    pItem = &pList->apEItem[pList->nEItem++];
+    pItem->zAs = tokenStr(p, pT);
+    pItem->pExpr = pExpr;
+    return pList;
+  }
+}
+
+%type lvalue {Expr*}
+lvalue(A) ::= ID(X).                   {A = idExpr(p,&X);}
+lvalue(A) ::= lvalue(X) DOT ID(Y).     {A = lvalueExpr(p,X,&Y);}
+lvalue(A) ::= lvalue(X) LB expr(Y) RB. {A = biExpr(p,X,TK_LB,Y);}
+
+%type expr {Expr*}
+expr(A) ::= lvalue(X).               {A = X;}
+expr(A) ::= jvalue(X).               {A = jsonExpr(p,X);}
+expr(A) ::= LC structlist(X) RC.     {A = stExpr(p,X);}
+expr(A) ::= LC RC.                   {A = stExpr(p,0);}
+expr(A) ::= LB arraylist(X) RB.      {A = arExpr(p,X);}
+expr(A) ::= LB RB.                   {A = arExpr(p,0);}
+
+%type structlist {ExprList*}
+structlist(A) ::= ID|STRING(Y) COLON expr(Z).    {A = apndExpr(p,0,Z,&Y);}
+structlist(A) ::= structlist(X) COMMA ID|STRING(Y) COLON expr(Z).
+                                                 {A = apndExpr(p,X,Z,&Y);}
+%type arraylist {ExprList*}
+arraylist(A) ::= expr(Y).                        {A = apndExpr(p,0,Y,0);}
+arraylist(A) ::= arraylist(X) COMMA expr(Y).     {A = apndExpr(p,X,Y,0);}
+
+
+
+expr(A) ::= ID(X) LP exprlist(Y) RP.  {A = funcExpr(p,&X,Y);}
+expr(A) ::= expr(X) AND(OP) expr(Y).  {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) OR(OP) expr(Y).              {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y).     {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) EQEQ|NE|EQ3|NE3(OP) expr(Y). {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) BITAND(OP) expr(Y).          {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) BITXOR(OP) expr(Y).          {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) BITOR(OP) expr(Y).           {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) LSHIFT|RSHIFT|URSHIFT(OP) expr(Y).
+                                                 {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y).      {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).  {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) IN|WITHIN(OP) expr(Y).       {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) ILIKEOP(OP) expr(Y).          {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= expr(X) LIKEOP(OP) expr(Y).          {A = biExpr(p,X,@OP,Y);}
+expr(A) ::= BANG(OP) expr(X).                    {A = biExpr(p,X,@OP,0);}
+expr(A) ::= BITNOT(OP) expr(X).                  {A = biExpr(p,X,@OP,0);}
+expr(A) ::= MINUS(OP) expr(X). [BITNOT]          {A = biExpr(p,X,@OP,0);}
+expr(A) ::= PLUS(OP) expr(X). [BITNOT]           {A = biExpr(p,X,@OP,0);}
+expr(A) ::= LP select(X) RP.                     {A = subqExpr(p,X);}
+expr(A) ::= LP expr(X) RP.                       {A = X;}
+expr(A) ::= expr(X) COLLATE ID|STRING.           {A = X;}
+expr(A) ::= expr(X) QM expr(Y) COLON expr(Z).    {A = triExpr(p,X,Y,Z);}
+
+%type exprlist {ExprList*}
+%type nexprlist {ExprList*}
+exprlist(A) ::= nexprlist(X).     {A = X;}
+exprlist(A) ::= .                 {A = 0;}
+nexprlist(A) ::= expr(X).                     {A = apndExpr(p,0,X,0);}
+nexprlist(A) ::= nexprlist(X) COMMA expr(Y).  {A = apndExpr(p,X,Y,0);}
+
+//////////////////////// The SELECT statement /////////////////////////////////
+//
+cmd(A) ::= select(X).  {
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_SELECT;
+    pNew->u.q.pQuery = X;
+  }
+  A = pNew;
+}
+
+%left UNION EXCEPT.
+%left INTERSECT.
+%type select {Query*}
+%type selectcore {Query*}
+%type compound {Query*}
+%type esel {Query*}
+%type x {Query*}
+
+%include {
+  /* The value of a LIMIT ... OFFSET ... clause. */
+  typedef struct LimitOffset {
+    Expr *pLimit;
+    Expr *pOffset;
+  } LimitOffset;
+
+  /* The value of a GROUP BY ... HAVING ... clause */
+  typedef struct GroupByHaving {
+    ExprList *pGroupBy;
+    Expr *pHaving;
+  } GroupByHaving;
+
+  /* Construct a simple query object */
+  static Query *simpleQuery(
+    Parse *p,
+    int isDistinct,
+    Expr *pRes,
+    const char *zAs,
+    DataSrc *pFrom,
+    Expr *pWhere,
+    GroupByHaving *pGroupBy
+  ){
+    Query *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eQType = TK_SELECT;
+      pNew->u.simple.isDistinct = isDistinct;
+      pNew->u.simple.pRes = pRes;
+      pNew->zAs = zAs;
+      pNew->u.simple.pFrom = pFrom;
+      pNew->u.simple.pWhere = pWhere;
+      pNew->u.simple.pGroupBy = pGroupBy ? pGroupBy->pGroupBy : 0;
+      pNew->u.simple.pHaving = pGroupBy ? pGroupBy->pHaving : 0;
+    }
+    return pNew;
+  }
+
+  /* Construct a compound query object */
+  static Query *compoundQuery(
+    Parse *p,
+    Query *pLeft,
+    int eOp,
+    Query *pRight
+  ){
+    Query *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eQType = eOp;
+      pNew->u.compound.pLeft = pLeft;
+      pNew->u.compound.pRight = pRight;
+      pNew->zAs = pLeft->zAs;
+    }
+    return pNew;
+  }
+}
+
+
+
+select(A) ::= compound(Q) orderby_opt(O) limit_opt(L). {
+  Q->pOrderBy = O;
+  Q->pLimit = L.pLimit;
+  Q->pOffset = L.pOffset;
+  A = Q;
+}
+compound(A) ::= selectcore(X).                 {A = X;}
+compound(A) ::= esel(X) UNION(OP) esel(Y).     {A=compoundQuery(p,X,@OP,Y);}
+compound(A) ::= esel(X) UNION ALL(OP) esel(Y). {A=compoundQuery(p,X,@OP,Y);}
+compound(A) ::= esel(X) EXCEPT(OP) esel(Y).    {A=compoundQuery(p,X,@OP,Y);}
+compound(A) ::= esel(X) INTERSECT(OP) esel(Y). {A=compoundQuery(p,X,@OP,Y);}
+esel(A) ::= compound(X).                       {A = X;}
+esel(A) ::= expr(X). {
+  /* TODO: Fix these error messages */
+  if( p->errCode==XJD1_OK ){
+    if( X->eClass!=XJD1_EXPR_Q ){
+      xjd1ParseError(p, XJD1_SYNTAX,
+        "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+      );
+    }else{
+      A = X->u.subq.p;
+      if( A->pOrderBy || A->pLimit ){
+        xjd1ParseError(p, XJD1_SYNTAX,
+          "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
+        );
+      }
+    }
+  }
+}
+
+selectcore(A) ::= SELECT 
+  dist_opt(D) sel_result(S) from(F) where_opt(W) groupby_opt(G).
+  {A = simpleQuery(p,D,S.pExpr,S.zAs,F,W,&G);}
+
+
+%type dist_opt {int}
+dist_opt(A) ::= .                    {A = 0;}
+dist_opt(A) ::= DISTINCT.            {A = 1;}
+dist_opt(A) ::= ALL.                 {A = 0;}
+
+// The result set of an expression can be either an JSON expression
+// or nothing. If it is a JSON expression, it may be given an alias
+// using the "AS <id>" syntax.
+//
+%type sel_result {ExprItem}
+sel_result(A) ::= .                       {A.pExpr=0;A.zAs=0;}
+sel_result(A) ::= expr(X).                {A.pExpr=X;A.zAs=0;}
+sel_result(A) ::= expr(X) AS ID(Y).       {A.pExpr=X;A.zAs=tokenStr(p, &Y);}
+
+// A complete FROM clause.
+//
+%type from {DataSrc*}
+%type fromlist {DataSrc*}
+%type fromitem {DataSrc*}
+%include {
+  /* Create a new data source that is a named table */
+  static DataSrc *pathDataSrc(Parse *p, Expr *pPath, Token *pAs){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      if( pPath ){
+        if( pPath->eType==TK_ID ){
+          pNew->eDSType = TK_ID;
+          pNew->u.tab.zName = pPath->u.id.zId;
+        }else{
+          pNew->eDSType = TK_DOT;
+          pNew->u.path.pPath = pPath;
+        }
+        pNew->zAs = tokenStr(p, pAs);
+      }else{
+        pNew->eDSType = TK_ID;
+        pNew->u.tab.zName = tokenStr(p,pAs);
+      }
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that is a join */
+  static DataSrc *joinDataSrc(Parse *p, DataSrc *pLeft, DataSrc *pRight){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_COMMA;
+      pNew->u.join.pLeft = pLeft;
+      pNew->u.join.pRight = pRight;
+    }
+    return pNew;
+  }
+
+  /* Create a new subquery data source */
+  static DataSrc *subqDataSrc(Parse *p, Query *pSubq, Token *pAs){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_SELECT;
+      pNew->u.subq.q = pSubq;
+      pNew->zAs = tokenStr(p, pAs);
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that is a FLATTEN or EACH operator */
+  static DataSrc *flattenDataSrc(
+    Parse *p,
+    DataSrc *pLeft,
+    Token *pOp,
+    Expr *pPath,
+    Expr *pAs
+  ){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_FLATTENOP;
+      pNew->u.flatten.pNext = pLeft;
+      pNew->u.flatten.cOpName = pOp->z[0];
+      pNew->u.flatten.pExpr = pPath;
+      pNew->u.flatten.pAs = (pAs ? pAs : pPath);
+    }
+    return pNew;
+  }
+
+  /* Create a new data source that represents an empty FROM clause.
+  ** This is used for queries of the form "SELECT <expr>". It returns a
+  ** single object with no properties.  
+  */
+  static DataSrc *nullDataSrc(Parse *p){
+    DataSrc *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eDSType = TK_NULL;
+    }
+    return pNew;
+  }
+}
+from(A) ::= .                                    {A = nullDataSrc(p);}
+from(A) ::= FROM fromlist(X).                    {A = X;}
+fromlist(A) ::= fromitem(X).                     {A = X;}
+fromlist(A) ::= fromlist(X) COMMA fromitem(Y).   {A = joinDataSrc(p,X,Y);}
+fromitem(A) ::= LP select(X) RP AS ID(Y).        {A = subqDataSrc(p,X,&Y);}
+
+fromitem(A) ::= ID(X).                           {A = pathDataSrc(p,0,&X);}
+fromitem(A) ::= path(X) AS ID(Y).                {A = pathDataSrc(p,X,&Y);}
+
+fromitem(A) ::= fromitem(W) FLATTENOP(X) LP path(Y) eachalias(Z) RP. {
+  A = flattenDataSrc(p,W,&X,Y,Z);
+}
+
+%type eachalias {Expr*}
+eachalias(A) ::= .                 {A=0;}
+eachalias(A) ::= AS ID|STRING(Y).  {A=idExpr(p,&Y);}
+
+%type path {Expr*}
+path(A) ::= ID(Y).                               {A = idExpr(p, &Y);        }
+path(A) ::= path(X) DOT ID(Y).                   {A = lvalueExpr(p, X, &Y); }
+path(A) ::= path(X) LB ID|STRING(Y) RB.          {A = lvalueExpr(p, X, &Y); }
+
+%type groupby_opt {GroupByHaving}
+groupby_opt(A) ::= .                            {A.pGroupBy=0; A.pHaving=0;}
+groupby_opt(A) ::= GROUP BY exprlist(X).        {A.pGroupBy=X; A.pHaving=0;}
+groupby_opt(A) ::= GROUP BY exprlist(X) HAVING expr(Y).
+                                                {A.pGroupBy=X; A.pHaving=Y;}
+
+%type orderby_opt {ExprList*}
+%type sortlist {ExprList*}
+orderby_opt(A) ::= .                          {A = 0;}
+orderby_opt(A) ::= ORDER BY sortlist(X).      {A = X;}
+sortlist(A) ::= sortlist(X) COMMA expr(Y) sortorder(Z).
+                                              {A = apndExpr(p,X,Y,Z.n?&Z:0);}
+sortlist(A) ::= expr(Y) sortorder(Z).         {A = apndExpr(p,0,Y,Z.n?&Z:0);}
+sortorder(A) ::= ASCENDING(X).    {A = X;}
+sortorder(A) ::= DESCENDING(X).   {A = X;}
+sortorder(A) ::= .                {A.z=""; A.n=0;}
+
+%type limit_opt {LimitOffset}
+limit_opt(A) ::= .                             {A.pLimit=0; A.pOffset=0;}
+limit_opt(A) ::= LIMIT expr(X).                {A.pLimit=X; A.pOffset=0;}
+limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y). {A.pLimit=X; A.pOffset=Y;}
+
+%type where_opt {Expr*}
+where_opt(A) ::= .                  {A = 0;}
+where_opt(A) ::= WHERE expr(X).     {A = X;}
+
+///////////////////// TRANSACTIONS ////////////////////////////
+//
+cmd ::= BEGIN ID.
+cmd ::= ROLLBACK ID.
+cmd ::= COMMIT ID.
+
+///////////////////// The CREATE COLLECTION statement ////////////////////////
+//
+cmd(A) ::= CREATE COLLECTION ifnotexists(B) tabname(N). {
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_CREATECOLLECTION;
+    pNew->u.crtab.ifExists = B;
+    pNew->u.crtab.zName = tokenStr(p, &N);
+  }
+  A = pNew;
+}
+%type ifnotexists {int}
+ifnotexists(A) ::= .                    {A = 0;}
+ifnotexists(A) ::= IF NOT EXISTS.       {A = 1;}
+tabname(A) ::= ID(X).                   {A = X;}
+
+////////////////////////// The DROP COLLECTION ///////////////////////////////
+//
+cmd(A) ::= DROP COLLECTION ifexists(B) tabname(N). {
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_DROPCOLLECTION;
+    pNew->u.crtab.ifExists = B;
+    pNew->u.crtab.zName = tokenStr(p, &N);
+  }
+  A = pNew;
+}
+%type ifexists {int}
+ifexists(A) ::= IF EXISTS.  {A = 1;}
+ifexists(A) ::= .           {A = 0;}
+
+
+/////////////////////////// The DELETE statement /////////////////////////////
+//
+cmd(A) ::= DELETE FROM tabname(N) where_opt(W). {
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_DELETE;
+    pNew->u.del.zName = tokenStr(p, &N);
+    pNew->u.del.pWhere = W;
+  }
+  A = pNew;
+}
+
+////////////////////////// The UPDATE command ////////////////////////////////
+//
+cmd(A) ::= UPDATE tabname(N) SET setlist(L) where_opt(W) upsert_opt(U). {
+  Command *pNew = xjd1PoolMalloc(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_UPDATE;
+    pNew->u.update.zName = tokenStr(p, &N);
+    pNew->u.update.pWhere = W;
+    pNew->u.update.pChng = L;
+    pNew->u.update.pUpsert = U;
+  }
+  A = pNew;
+}
+
+%type setlist {ExprList*}
+setlist(A) ::= setlist(X) COMMA lvalue(Y) EQ expr(Z). {
+   A = apndExpr(p,X,Y,0);
+   A = apndExpr(p,A,Z,0);
+}
+setlist(A) ::= lvalue(Y) EQ expr(Z). {
+   A = apndExpr(p,0,Y,0);
+   A = apndExpr(p,A,Z,0);
+}
+
+%type upsert_opt {Expr*}
+upsert_opt(A) ::= .                       {A = 0;}
+upsert_opt(A) ::= ELSE INSERT expr(X).    {A = X;}
+
+
+
+////////////////////////// The INSERT command /////////////////////////////////
+//
+cmd(A) ::= async INSERT INTO tabname(N) VALUE expr(V). {
+  Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_INSERT;
+    pNew->u.ins.zName = tokenStr(p, &N);
+    pNew->u.ins.pValue = V;
+  }
+  A = pNew;
+}
+cmd(A) ::= async INSERT INTO tabname(N) select(Q). {
+  Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+  if( pNew ){
+    pNew->eCmdType = TK_INSERT;
+    pNew->u.ins.zName = tokenStr(p, &N);
+    pNew->u.ins.pQuery = Q;
+  }
+  A = pNew;
+}
+async ::= .
+async ::= ASYNC.
+async ::= SYNC.
+
+////////////////////////// The PRAGMA command /////////////////////////////////
+//
+%include {
+  static Command *makePrag(Parse *p, Token *pName, Expr *pValue){
+    Command *pNew = xjd1PoolMallocZero(p->pPool, sizeof(*pNew));
+    if( pNew ){
+      pNew->eCmdType = TK_PRAGMA;
+      pNew->u.prag.zName = tokenStr(p, pName);
+      pNew->u.prag.pValue = pValue;
+    }
+    return pNew;
+  }
+}
+cmd(A) ::= PRAGMA ID(N).                {A = makePrag(p,&N,0);}
+cmd(A) ::= PRAGMA ID(N) EQ expr(V).     {A = makePrag(p,&N,V);}
+cmd(A) ::= PRAGMA ID(N) LP expr(V) RP.  {A = makePrag(p,&N,V);}

+ 32 - 0
unql/src/pragma.c

@@ -0,0 +1,32 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Code to evaluate a pragma.
+*/
+#include "xjd1Int.h"
+
+/*
+** Evaluate a pragma.
+**
+** Unknown pragmas are silently ignored.
+*/
+int xjd1PragmaStep(xjd1_stmt *pStmt){
+  Command *pCmd = pStmt->pCmd;
+  int rc = XJD1_OK;
+  assert( pCmd!=0 );
+  assert( pCmd->eCmdType==TK_PRAGMA );
+  return rc;
+}

+ 774 - 0
unql/src/query.c

@@ -0,0 +1,774 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code used to implement query processing.
+*/
+#include "xjd1Int.h"
+
+
+struct ResultItem {
+  JsonNode **apKey;               /* Array of JSON objects */
+  ResultItem *pNext;              /* Next element in list */
+};
+
+static int addToResultList(
+  ResultList *pList,              /* List to append to */
+  JsonNode **apKey                /* Array of values to add to list */
+){
+  ResultItem *pNew;               /* Newly allocated ResultItem */
+  int nByte;                      /* Bytes to allocate for new node */
+  int i;                          /* Used to iterate through apKey[] */
+
+  nByte = sizeof(ResultItem) + sizeof(JsonNode*) * pList->nKey;
+  pNew = (ResultItem *)xjd1PoolMalloc(pList->pPool, nByte);
+  if( !pNew ){
+    for(i=0; i<pList->nKey; i++){
+      xjd1JsonFree(apKey[i]);
+    }
+    return XJD1_NOMEM;
+  }
+
+  pNew->apKey = (JsonNode **)&pNew[1];
+  memcpy(pNew->apKey, apKey, pList->nKey * sizeof(JsonNode *));
+  pNew->pNext = pList->pItem;
+  pList->pItem = pNew;
+
+  return XJD1_OK;
+}
+
+static int cmpResultItem(ResultItem *p1, ResultItem *p2, ExprList *pEList){
+  int i;
+  int c = 0;
+  int nEItem = (pEList ? pEList->nEItem : 1);
+  for(i=0; i<nEItem; i++){
+    if( 0!=(c=xjd1JsonCompare(p1->apKey[i], p2->apKey[i], 0)) ) break;
+  }
+  if( c && pEList ){
+    char const *zDir = pEList->apEItem[i].zAs;
+    if( zDir && zDir[0]=='D' ){
+      c = c*-1;
+    }
+  }
+  return c;
+}
+
+static ResultItem *mergeResultItems(
+  ExprList *pEList,              /* Used for ASC/DESC of each key */
+  ResultItem *p1,                /* First list to merge */
+  ResultItem *p2                 /* Second list to merge */
+){
+  ResultItem *pRet = 0;
+  ResultItem **ppNext;
+
+  ppNext = &pRet;
+  while( p1 || p2 ){
+    if( !p1 ){
+      *ppNext = p2;
+      p2 = 0;
+    }else if( !p2 ){
+      *ppNext = p1;
+      p1 = 0;
+    }else{
+      int c = cmpResultItem(p1, p2, pEList);
+      if( c<=0 ){
+        *ppNext = p1;
+        ppNext = &p1->pNext;
+        p1 = p1->pNext;
+      }else{
+        *ppNext = p2;
+        ppNext = &p2->pNext;
+        p2 = p2->pNext;
+      }
+    }
+  }
+
+  return pRet;
+}
+
+static void sortResultList(ResultList *pList, ExprList *pEList, int uniq){
+  int i;                          /* Used to iterate through aList[] */
+  ResultItem *aList[40];          /* Array of slots for merge sort */
+  ResultItem *pHead;
+
+  memset(aList, 0, sizeof(aList));
+  pHead = pList->pItem;
+
+  while( pHead ){
+    ResultItem *pNext = pHead->pNext;
+    pHead->pNext = 0;
+    for(i=0; aList[i]; i++){
+      assert( i<ArraySize(aList) );
+      pHead = mergeResultItems(pEList, pHead, aList[i]);
+      aList[i] = 0;
+    }
+    aList[i] = pHead;
+    pHead = pNext;
+  }
+
+  pHead = aList[0];
+  for(i=1; i<ArraySize(aList); i++){
+    pHead = mergeResultItems(pEList, pHead, aList[i]);
+  }
+  pList->pItem = pHead;
+
+  if( uniq && pHead ){
+    ResultItem *pPrev = pHead;
+    ResultItem *p;
+    for(p=pPrev->pNext; p; p=p->pNext){
+      if( cmpResultItem(pPrev, p, pEList)==0 ){
+        pPrev->pNext = p->pNext;
+      }else{
+        pPrev = p;
+      }
+    }
+  }
+
+#if 1
+  while( pHead ){
+    ResultItem *pNext = pHead->pNext;
+    assert( pNext==0 || cmpResultItem(pHead, pNext, pEList)<=(uniq?-1:0) );
+    pHead = pNext;
+  }
+#endif
+}
+
+static void freeResultListItem(ResultList *pList, ResultItem *pItem){
+  int i;
+  for(i=0; i<pList->nKey; i++){
+    xjd1JsonFree(pItem->apKey[i]);
+  }
+}
+
+static void popResultList(ResultList *pList){
+  ResultItem *pItem;
+  pItem = pList->pItem;
+  if( pItem ){
+    pList->pItem = pItem->pNext;
+    freeResultListItem(pList, pItem);
+  }
+}
+
+/*
+** These two are used GROUP BY processing.
+*/
+static void saveResultList(ResultList *pList){
+  if( pList->pSaved ){
+    freeResultListItem(pList, pList->pSaved);
+  }
+  pList->pSaved = pList->pItem;
+  pList->pItem = pList->pItem->pNext;
+}
+static void restoreResultList(ResultList *pList){
+  if( pList->pSaved ){
+    popResultList(pList);
+    pList->pSaved->pNext = pList->pItem;
+    pList->pItem = pList->pSaved;
+    pList->pSaved = 0;
+  }
+}
+
+
+static void clearResultList(ResultList *pList){
+  while( pList->pItem ) popResultList(pList);
+  xjd1PoolDelete(pList->pPool);
+  memset(pList, 0, sizeof(ResultList));
+}
+
+/*
+** Called after statement parsing to initalize every Query object
+** within the statement.
+*/
+int xjd1QueryInit(
+  Query *p,                       /* Query to initialize */
+  xjd1_stmt *pStmt,               /* Statement this query is part of */
+  void *pCtx                      /* Context for expression initialization */
+){
+  int rc;
+  if( p==0 ) return XJD1_OK;
+  p->pStmt = pStmt;
+  if( p->eQType==TK_SELECT ){
+    rc = xjd1ExprInit(p->u.simple.pRes, pStmt, p, XJD1_EXPR_RESULT, pCtx);
+    if( !rc ){
+      rc = xjd1DataSrcInit(p->u.simple.pFrom, p, pCtx);
+    }
+    if( !rc ){
+      rc = xjd1ExprInit(p->u.simple.pWhere, pStmt, p, XJD1_EXPR_WHERE, pCtx);
+    }
+    if( !rc ){
+      rc = xjd1ExprListInit(
+          p->u.simple.pGroupBy, pStmt, p, XJD1_EXPR_GROUPBY, pCtx
+      );
+    }
+    if( !rc ){
+      rc = xjd1ExprInit(p->u.simple.pHaving, pStmt, p, XJD1_EXPR_HAVING, pCtx);
+    }
+    if( !rc && p->u.simple.pGroupBy ){
+      rc = xjd1AggregateInit(pStmt, p, 0);
+    }
+  }else{
+    rc = xjd1QueryInit(p->u.compound.pLeft, pStmt, pCtx);
+    if( !rc ) rc = xjd1QueryInit(p->u.compound.pRight, pStmt, pCtx);
+  }
+
+  if( !rc ){
+    rc = xjd1ExprListInit(p->pOrderBy, pStmt, p, XJD1_EXPR_ORDERBY, pCtx);
+  }
+  if( !rc ){
+    rc = xjd1ExprInit(p->pLimit, pStmt, p, XJD1_EXPR_LIMIT, pCtx);
+  }
+  if( !rc ){
+    rc = xjd1ExprInit(p->pOffset, pStmt, p, XJD1_EXPR_OFFSET, pCtx);
+  }
+  return rc;
+}
+
+/*
+** Rewind a query so that it is pointing at the first row.
+*/
+int xjd1QueryRewind(Query *p){
+  if( p==0 ) return XJD1_OK;
+  if( p->eQType==TK_SELECT ){
+    xjd1DataSrcRewind(p->u.simple.pFrom);
+    clearResultList(&p->u.simple.grouped);
+    clearResultList(&p->u.simple.distincted);
+    xjd1AggregateClear(p);
+  }else{
+    xjd1QueryRewind(p->u.compound.pLeft);
+    xjd1QueryRewind(p->u.compound.pRight);
+    p->u.compound.doneLeft = 0;
+    clearResultList(&p->u.compound.left);
+    clearResultList(&p->u.compound.right);
+    xjd1JsonFree(p->u.compound.pOut);
+    p->u.compound.pOut = 0;
+  }
+  clearResultList(&p->ordered);
+  p->eDocFrom = XJD1_FROM_DATASRC;
+  p->bLimitValid = 0;
+  return XJD1_OK;
+}
+
+/*
+** Advance to the next row of the TK_SELECT query passed as the first
+** argument, disregarding any ORDER BY, OFFSET or LIMIT clause.
+**
+** Return XJD1_ROW if there is such a row, or XJD1_DONE at EOF. Or return
+** an error code if an error occurs.
+*/
+static int selectStepWhered(Query *p){
+  int rc;                         /* Return code */
+  assert( p->eQType==TK_SELECT );
+  do{
+    rc = xjd1DataSrcStep(p->u.simple.pFrom);
+  }while(
+    rc==XJD1_ROW
+    && (p->u.simple.pWhere!=0 && !xjd1ExprTrue(p->u.simple.pWhere))
+  );
+  return rc;
+}
+
+static int selectStepGrouped(Query *p){
+  Aggregate *pAgg = p->u.simple.pAgg;
+  int rc;
+
+  if( pAgg ){
+    ExprList *pGroupBy = p->u.simple.pGroupBy;
+
+    rc = XJD1_OK;
+    if( pGroupBy==0 ){
+      /* An aggregate query with no GROUP BY clause. If there is no GROUP BY,
+      ** then exactly one row is returned, which makes ORDER BY and DISTINCT
+      ** no-ops. And it is not possible to have a HAVING clause without a
+      ** GROUP BY, so no need to worry about that either.
+      */
+      assert( p->u.simple.pHaving==0 );
+
+      if( p->u.simple.grouped.pPool==0 ){
+        JsonNode **apSrc;
+        int nSrc;
+        int saved = 0;
+        Pool *pPool;
+
+        pPool = p->u.simple.grouped.pPool = xjd1PoolNew();
+        if( !pPool ) return XJD1_NOMEM;
+
+        p->u.simple.grouped.nKey = nSrc = xjd1DataSrcCount(p->u.simple.pFrom);
+        apSrc = (JsonNode **)xjd1PoolMallocZero(pPool, nSrc*sizeof(JsonNode *));
+        if( !apSrc ) return XJD1_NOMEM;
+
+        /* Call the xStep() of each aggregate in the query for each row
+        ** matched by the query WHERE clause. */
+        while( rc==XJD1_OK && XJD1_ROW==(rc = selectStepWhered(p) ) ){
+          int saveThisRow = 0;
+          rc = xjd1AggregateStep(pAgg, &saveThisRow);
+          if( saved==0 || saveThisRow ){
+            xjd1DataSrcCacheSave(p->u.simple.pFrom, apSrc);
+            saved = 1;
+          }
+        }
+        assert( rc!=XJD1_OK );
+        if( rc==XJD1_DONE ){
+          rc = addToResultList(&p->u.simple.grouped, apSrc);
+        }
+        if( rc==XJD1_OK ){
+          rc = xjd1AggregateFinalize(pAgg);
+        }
+        if( rc==XJD1_OK ){
+          rc = XJD1_ROW;
+          p->eDocFrom = XJD1_FROM_GROUPED;
+        }
+
+      }else{
+        rc = XJD1_DONE;
+      }
+
+      /* TODO: If this is a top-level query, then xQueryDoc(p, 0) will be
+      ** called exactly once to retrieve the query result. When any
+      ** aggregate expressions within the query result are evaluated, the
+      ** xFinal function is evaluated. But this will only work once - if
+      ** xQueryDoc(p, 0) is called more than once it will break. Find out if
+      ** this is a problem!
+      */
+
+    }else{
+
+      /* An aggregate with a GROUP BY clause. There may also be a DISTINCT
+      ** qualifier.
+      **
+      ** Use a ResultList to sort all the rows matched by the WHERE
+      ** clause. The apKey[] array consists of each of the expressions
+      ** in the GROUP BY clause, followed by each document in the FROM
+      ** clause. */
+      do {
+        ResultItem *pItem;
+
+        if( p->u.simple.grouped.pPool==0 ){
+          DataSrc *pFrom = p->u.simple.pFrom;
+          JsonNode **apKey;
+          int nByte;
+          Pool *pPool;
+
+          /* Allocate the memory pool for this ResultList. And apKey. */
+          pPool = p->u.simple.grouped.pPool = xjd1PoolNew();
+          p->u.simple.grouped.nKey = pGroupBy->nEItem + xjd1DataSrcCount(pFrom);
+          if( !pPool ) return XJD1_NOMEM;
+          nByte = p->u.simple.grouped.nKey * sizeof(JsonNode *);
+          apKey = (JsonNode **)xjd1PoolMallocZero(pPool, nByte);
+          if( !apKey ) return XJD1_NOMEM;
+
+          while( rc==XJD1_OK && XJD1_ROW==(rc = selectStepWhered(p) ) ){
+            int i;
+            for(i=0; i<pGroupBy->nEItem; i++){
+              apKey[i] = xjd1ExprEval(pGroupBy->apEItem[i].pExpr);
+            }
+            xjd1DataSrcCacheSave(pFrom, &apKey[i]);
+            rc = addToResultList(&p->u.simple.grouped, apKey);
+            memset(apKey, 0, nByte);
+          }
+          if( rc!=XJD1_DONE ) return rc;
+          sortResultList(&p->u.simple.grouped, pGroupBy, 0);
+        }else{
+          popResultList(&p->u.simple.grouped);
+        }
+
+        p->eDocFrom = XJD1_FROM_GROUPED;
+        pItem = p->u.simple.grouped.pItem;
+        if( pItem==0 ){
+          rc = XJD1_DONE;
+        }else{
+          while( 1 ){
+            int saveThisRow = 0;
+            ResultItem *pNext = pItem->pNext;
+            rc = xjd1AggregateStep(pAgg, &saveThisRow);
+            if( saveThisRow ) saveResultList(&p->u.simple.grouped);
+            if( rc || !pNext || cmpResultItem(pItem, pNext, pGroupBy) ){
+              restoreResultList(&p->u.simple.grouped);
+              break;
+            }
+            if( !saveThisRow ) popResultList(&p->u.simple.grouped);
+            pItem = p->u.simple.grouped.pItem;
+          }
+
+          if( XJD1_OK==rc && XJD1_OK==(rc = xjd1AggregateFinalize(pAgg)) ){
+            rc = XJD1_ROW;
+          }
+        }
+
+      }while( rc==XJD1_ROW
+           && p->u.simple.pHaving && !xjd1ExprTrue(p->u.simple.pHaving)
+      );
+    }
+
+  }else{
+    /* Non-aggregate query */
+    rc = selectStepWhered(p);
+  }
+
+  return rc;
+}
+
+static int selectStepDistinct(Query *p){
+  int rc;
+  if( p->u.simple.isDistinct ){
+    if( p->u.simple.distincted.pPool==0 ){
+      Pool *pPool;
+      JsonNode **apKey;
+      int nKey;
+
+      nKey = 1 + xjd1DataSrcCount(p->u.simple.pFrom);
+      pPool = p->u.simple.distincted.pPool = xjd1PoolNew();
+      if( !pPool ) return XJD1_NOMEM;
+      p->u.simple.distincted.nKey = nKey;
+      apKey = xjd1PoolMallocZero(pPool, nKey * sizeof(JsonNode *));
+      if( !apKey ) return XJD1_NOMEM;
+
+      while( XJD1_ROW==(rc = selectStepGrouped(p) ) ){
+        apKey[0] = xjd1QueryDoc(p, 0);
+        xjd1DataSrcCacheSave(p->u.simple.pFrom, &apKey[1]);
+        rc = addToResultList(&p->u.simple.distincted, apKey);
+        memset(apKey, 0, nKey * sizeof(JsonNode *));
+        if( rc!=XJD1_OK ) break;
+      }
+      if( rc==XJD1_DONE ){
+        sortResultList(&p->u.simple.distincted, 0, 1);
+        p->eDocFrom = XJD1_FROM_DISTINCTED;
+        rc = XJD1_ROW;
+      }
+    }else{
+      popResultList(&p->u.simple.distincted);
+      rc = p->u.simple.distincted.pItem ? XJD1_ROW : XJD1_DONE;
+    }
+  }else{
+    rc = selectStepGrouped(p);
+
+
+  }
+  return rc;
+}
+
+
+static int selectStepCompounded(Query *);
+static int cacheQuery(ResultList *pList, Query *p){
+  Pool *pPool;
+  int rc;
+
+  pList->nKey = 1;
+  pPool = pList->pPool = xjd1PoolNew();
+  if( !pPool ) return XJD1_NOMEM;
+
+  while( XJD1_ROW==(rc = selectStepCompounded(p) ) ){
+    JsonNode *pDoc = xjd1QueryDoc(p, 0);
+    rc = addToResultList(pList, &pDoc);
+    if( rc!=XJD1_OK ) break;
+  }
+
+  if( rc==XJD1_DONE ){
+    sortResultList(pList, 0, 1);
+    rc = XJD1_OK;
+  }
+  return rc;
+}
+
+
+static int selectStepCompounded(Query *p){
+  int rc;
+  if( p->eQType==TK_SELECT ){
+    rc = selectStepDistinct(p);
+  }else{
+    JsonNode *pOut = 0;
+    if( p->eQType==TK_ALL ){
+      rc = XJD1_DONE;
+      if( p->u.compound.doneLeft==0 ){
+        rc = selectStepCompounded(p->u.compound.pLeft);
+        if( rc==XJD1_ROW ){
+          pOut = xjd1QueryDoc(p->u.compound.pLeft, 0);
+        }
+      }
+      if( rc==XJD1_DONE ){
+        p->u.compound.doneLeft = 1;
+        rc = selectStepCompounded(p->u.compound.pRight);
+        if( rc==XJD1_ROW ){
+          pOut = xjd1QueryDoc(p->u.compound.pRight, 0);
+        }
+      }
+    }else{
+
+      ResultList *p1;
+      ResultList *p2;
+
+      if( p->u.compound.left.pPool==0 ){
+        rc = cacheQuery(&p->u.compound.left, p->u.compound.pLeft);
+        if( rc!=XJD1_OK ) return rc;
+        rc = cacheQuery(&p->u.compound.right, p->u.compound.pRight);
+        if( rc!=XJD1_OK ) return rc;
+      }
+
+      p1 = &p->u.compound.left;
+      p2 = &p->u.compound.right;
+      rc = XJD1_DONE;
+
+      switch( p->eQType ){
+        case TK_UNION: {
+          if( (p1->pItem || p2->pItem) ){
+            int c;
+            if( p1->pItem==0 ){
+              c = 1;
+            }else if( p2->pItem==0 ){
+              c = -1;
+            }else{
+              c = cmpResultItem(p1->pItem, p2->pItem, 0);
+            }
+
+            if( c<0 ){
+              pOut = xjd1JsonRef(p1->pItem->apKey[0]);
+            }else{
+              pOut = xjd1JsonRef(p2->pItem->apKey[0]);
+            }
+            if( c<=0 ) popResultList(p1);
+            if( c>=0 ) popResultList(p2);
+            rc = XJD1_ROW;
+          }
+          break;
+        }
+
+        case TK_INTERSECT:
+          while( rc==XJD1_DONE && p1->pItem && p2->pItem ){
+            int c = cmpResultItem(p1->pItem, p2->pItem, 0);
+            if( c==0 ){
+              pOut = xjd1JsonRef(p1->pItem->apKey[0]);
+              rc = XJD1_ROW;
+            }
+            if( c<=0 ) popResultList(p1);
+            if( c>=0 ) popResultList(p2);
+          }
+          break;
+
+        case TK_EXCEPT:
+          while( rc==XJD1_DONE && p1->pItem ){
+            int c = p2->pItem ? cmpResultItem(p1->pItem, p2->pItem, 0) : -1;
+            if( c<=0 ){
+              if( c ){
+                pOut = xjd1JsonRef(p1->pItem->apKey[0]);
+                rc = XJD1_ROW;
+              }
+              popResultList(p1);
+            }else{
+              popResultList(p2);
+            }
+          }
+          break;
+      }
+    }
+
+    xjd1JsonFree(p->u.compound.pOut);
+    p->u.compound.pOut = pOut;
+  }
+  return rc;
+}
+
+/*
+** Advance to the next row of the TK_SELECT query passed as the first
+** argument, disregarding any OFFSET or LIMIT clause.
+**
+** Return XJD1_ROW if there is such a row, or XJD1_EOF if there is not. Or
+** return an error code if an error occurs.
+*/
+static int selectStepOrdered(Query *p){
+  int rc = XJD1_OK;                    /* Return Code */
+  ExprList *pOrderBy = p->pOrderBy;    /* ORDER BY clause (or NULL) */
+
+  if( pOrderBy ){
+
+    /* A non-aggregate with an ORDER BY clause. */
+    if( p->ordered.pPool==0 ){
+      int nKey = pOrderBy->nEItem + 1;
+      Pool *pPool;
+      JsonNode **apKey;
+
+      p->ordered.nKey = nKey;
+      pPool = p->ordered.pPool = xjd1PoolNew();
+      if( !pPool ) return XJD1_NOMEM;
+      apKey = xjd1PoolMallocZero(pPool, nKey * sizeof(JsonNode *));
+      if( !apKey ) return XJD1_NOMEM;
+
+      while( XJD1_ROW==(rc = selectStepCompounded(p) ) ){
+        int i;
+        for(i=0; i<pOrderBy->nEItem; i++){
+          apKey[i] = xjd1ExprEval(pOrderBy->apEItem[i].pExpr);
+        }
+        apKey[i] = xjd1QueryDoc(p, 0);
+
+        rc = addToResultList(&p->ordered, apKey);
+        if( rc!=XJD1_OK ) break;
+      }
+      if( rc!=XJD1_DONE ) return rc;
+
+      sortResultList(&p->ordered, pOrderBy, 0);
+      p->eDocFrom = XJD1_FROM_ORDERED;
+    }else{
+      assert( p->eDocFrom==XJD1_FROM_ORDERED );
+      popResultList(&p->ordered);
+    }
+
+    rc = p->ordered.pItem ? XJD1_ROW : XJD1_DONE;
+  }else{
+    /* No ORDER BY clause. */
+    rc = selectStepCompounded(p);
+  }
+
+  return rc;
+}
+
+/*
+** Advance a query to the next row.  Return XDJ1_DONE if there is no
+** next row, or XJD1_ROW if the step was successful.
+*/
+int xjd1QueryStep(Query *p){
+  int rc = XJD1_ROW;
+  if( p==0 ) return XJD1_DONE;
+
+  /* Query is either a simple SELECT, or a compound of some type. */
+  assert( p->eQType==TK_SELECT || p->eQType==TK_UNION || p->eQType==TK_ALL
+       || p->eQType==TK_EXCEPT || p->eQType==TK_INTERSECT
+  );
+
+  if( p->bLimitValid==0 ){
+    /* Calculate the values, if any, of the LIMIT and OFFSET clauses.
+    **
+    ** TBD: Should this throw an exception if the result of evaluating
+    ** either of these clauses cannot be converted to a number?
+    */
+    Expr *pLimit = p->pLimit;     /* The LIMIT expression, or NULL */
+    Expr *pOffset = p->pOffset;   /* The OFFSET expression, or NULL */
+
+    if( pOffset ){
+      JsonNode *pVal;             /* Result of evaluating expression pOffset */
+      double rOffset;             /* pVal converted to a number */
+
+      pVal = xjd1ExprEval(pOffset);
+      if( 0==xjd1JsonToReal(pVal, &rOffset) ){
+        int nOffset;
+        for(nOffset=(int)rOffset; nOffset>0; nOffset--){
+          rc = selectStepOrdered(p);
+          if( rc!=XJD1_ROW ) break;
+        }
+      }
+      xjd1JsonFree(pVal);
+    }
+
+    p->nLimit = -1;
+    if( p->pLimit ){
+      double rLimit;
+      JsonNode *pVal;
+
+      pVal = xjd1ExprEval(pLimit);
+      if( 0==xjd1JsonToReal(pVal, &rLimit) ){
+        p->nLimit = (int)rLimit;
+      }
+      xjd1JsonFree(pVal);
+    }
+    p->bLimitValid = 1;
+  }
+
+  if( rc==XJD1_ROW ){
+    if( p->nLimit==0 ){
+      rc = XJD1_DONE;
+    }else{
+      rc = selectStepOrdered(p);
+      if( p->nLimit>0 ) p->nLimit--;
+    }
+  }
+
+  return rc;
+}
+
+/*
+** Return a document currently referenced by a query.  If zDocName==0 then
+** return the constructed result set of the query.
+**
+** The caller must invoke JsonFree() when it is done with this value.
+*/
+JsonNode *xjd1QueryDoc(Query *p, int iDoc){
+  JsonNode *pOut = 0;
+  if( p ){
+    if( p->eDocFrom==XJD1_FROM_ORDERED ){
+      assert( iDoc==0 && p->ordered.pItem );
+      pOut = xjd1JsonRef(p->ordered.pItem->apKey[p->ordered.nKey-1]);
+    }else if( p->eQType==TK_SELECT ){
+      switch( p->eDocFrom ){
+        case XJD1_FROM_DISTINCTED:
+          pOut = xjd1JsonRef(p->u.simple.distincted.pItem->apKey[iDoc]);
+          break;
+
+        case XJD1_FROM_GROUPED:
+          if( iDoc==0 && p->u.simple.pRes ){
+            pOut = xjd1ExprEval(p->u.simple.pRes);
+          }else{
+            JsonNode **apSrc = p->u.simple.grouped.pItem->apKey;
+            if( p->u.simple.pGroupBy ){
+              apSrc = &apSrc[p->u.simple.pGroupBy->nEItem];
+            }
+            pOut = xjd1JsonRef(apSrc[(iDoc?iDoc-1:0)]);
+          }
+          break;
+
+        case XJD1_FROM_DATASRC:
+          if( iDoc==0 && p->u.simple.pRes ){
+            pOut = xjd1ExprEval(p->u.simple.pRes);
+          }else{
+            pOut = xjd1DataSrcRead(p->u.simple.pFrom, (iDoc ? iDoc : 1));
+          }
+          break;
+      }
+    }else{
+      assert( iDoc==0 );
+      pOut = xjd1JsonRef(p->u.compound.pOut);
+    }
+
+
+  }
+  return pOut;
+}
+
+
+/*
+** The destructor for a Query object.
+*/
+int xjd1QueryClose(Query *pQuery){
+  int rc = XJD1_OK;
+  if( pQuery==0 ) return rc;
+  if( pQuery->eQType==TK_SELECT ){
+    clearResultList(&pQuery->ordered);
+    clearResultList(&pQuery->u.simple.grouped);
+    clearResultList(&pQuery->u.simple.distincted);
+    xjd1ExprClose(pQuery->u.simple.pRes);
+    xjd1DataSrcClose(pQuery->u.simple.pFrom);
+    xjd1ExprClose(pQuery->u.simple.pWhere);
+    xjd1ExprListClose(pQuery->u.simple.pGroupBy);
+    xjd1ExprClose(pQuery->u.simple.pHaving);
+  }else{
+    xjd1QueryClose(pQuery->u.compound.pLeft);
+    xjd1QueryClose(pQuery->u.compound.pRight);
+    clearResultList(&pQuery->u.compound.left);
+    clearResultList(&pQuery->u.compound.right);
+    xjd1JsonFree(pQuery->u.compound.pOut);
+    pQuery->u.compound.pOut = 0;
+  }
+  xjd1ExprListClose(pQuery->pOrderBy);
+  xjd1ExprClose(pQuery->pLimit);
+  xjd1ExprClose(pQuery->pOffset);
+  return rc;
+}
+

+ 704 - 0
unql/src/shell.c

@@ -0,0 +1,704 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code that implements a simple command-line
+** wrapper shell around xjd1.
+*/
+#include "xjd1Int.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+/*
+** Remove n elements from argv beginning with the i-th element.
+*/
+void remove_from_argv(char **argv, int *pArgc, int i, int n){
+  int j;
+  int argc = *pArgc;
+  for(j=i+n; j<argc; i++, j++){
+    argv[i] = argv[j];
+  }
+  *pArgc = i;
+}
+
+
+/*
+** Look for a command-line option.  If present, return a pointer.
+** Return NULL if missing.
+**
+** hasArg==0 means the option is a flag.  It is either present or not.
+** hasArg==1 means the option has an argument.  Return a pointer to the
+** argument.
+*/
+const char *find_option(
+  char **argv,
+  int *pArgc,
+  const char *zLong,
+  const char *zShort,
+  int hasArg
+){
+  int i;
+  int nLong;
+  const char *zReturn = 0;
+  int argc = *pArgc;
+  assert( hasArg==0 || hasArg==1 );
+  nLong = strlen(zLong);
+  for(i=1; i<argc; i++){
+    char *z;
+    if (i+hasArg >= argc) break;
+    z = argv[i];
+    if( z[0]!='-' ) continue;
+    z++;
+    if( z[0]=='-' ){
+      if( z[1]==0 ){
+        remove_from_argv(argv, &argc, i, 1);
+        break;
+      }
+      z++;
+    }
+    if( strncmp(z,zLong,nLong)==0 ){
+      if( hasArg && z[nLong]=='=' ){
+        zReturn = &z[nLong+1];
+        remove_from_argv(argv, &argc, i, 1);
+        break;
+      }else if( z[nLong]==0 ){
+        zReturn = argv[i+hasArg];
+        remove_from_argv(argv, &argc, i, 1+hasArg);
+        break;
+      }
+    }else if( zShort && strcmp(z,zShort)==0 ){
+      zReturn = argv[i+hasArg];
+      remove_from_argv(argv, &argc, i, 1+hasArg);
+      break;
+    }
+  }
+  *pArgc = argc;
+  return zReturn;
+}
+
+/*
+** State information
+*/
+typedef struct Shell Shell;
+struct Shell {
+  xjd1 *pDb;           /* Open database connection */
+  const char *zFile;   /* Current filename */
+  int nLine;           /* Current line number */
+  FILE *pIn;           /* Input file */
+  int isTTY;           /* True if pIn is a TTY */
+  int shellFlags;      /* Flag settings */
+  int testErrcode;     /* Test case error code */
+  String testOut;      /* Output from a test case */
+  char zModule[50];    /* Mame of current test module */
+  char zTestCase[50];  /* Name of current testcase */
+  String inBuf;        /* Buffered input text */
+  int nCase;           /* Number of --testcase commands seen */
+  int nTest;           /* Number of tests performed */
+  int nErr;            /* Number of test errors */
+};
+
+/*
+** Flag names
+*/
+#define SHELL_PARSER_TRACE     0x00001
+#define SHELL_CMD_TRACE        0x00002
+#define SHELL_ECHO             0x00004
+#define SHELL_TEST_MODE        0x00008
+static const struct {
+  const char *zName;
+  int iValue;
+} FlagNames[] = {
+  {  "parser-trace",   SHELL_PARSER_TRACE },
+  {  "cmd-trace",      SHELL_CMD_TRACE    },
+  {  "echo",           SHELL_ECHO         },
+  {  "test-mode",      SHELL_TEST_MODE    },
+};
+
+/*
+** Convert a flag name into a mask.
+*/
+static int flagMask(const char *zName){
+  int i;
+  for(i=0; i<sizeof(FlagNames)/sizeof(FlagNames[0]); i++){
+    if( strcmp(zName, FlagNames[i].zName)==0 ){
+      return FlagNames[i].iValue;
+    }
+  }
+  return 0;
+}
+
+/*
+** True for space characters.
+*/
+static int shellIsSpace(char c){
+  return c==' ' || c=='\t' || c=='\r' || c=='\n';
+}
+
+/*
+** Set flags
+*/
+static int shellSet(Shell *p, int argc, char **argv){
+  int i;
+  if( argc>=2 ){
+    p->shellFlags |= flagMask(argv[1]);
+  }else{
+    for(i=0; i<sizeof(FlagNames)/sizeof(FlagNames[0]); i++){
+      printf("%-20s %s\n", 
+        FlagNames[i].zName,
+        (p->shellFlags & FlagNames[i].iValue)!=0 ? "on" : "off");
+    }
+  }
+  return 0;
+}
+static int shellClear(Shell *p, int argc, char **argv){
+  if( argc>=2 ){
+    p->shellFlags &= ~flagMask(argv[1]);
+  }
+  return 0;
+}
+
+/*
+** Command:  .quit
+*/
+static int shellQuit(Shell *p, int argc, char **argv){
+  return 1;
+}
+
+/*
+** Command:  .testcase NAME
+*/
+static int shellTestcase(Shell *p, int argc, char **argv){
+  if( argc>=2 ){
+    int n = strlen(argv[1]);
+    if( n>=sizeof(p->zTestCase) ) n = sizeof(p->zTestCase)-1;
+    memcpy(p->zTestCase, argv[1], n);
+    p->zTestCase[n] = 0;
+    xjd1StringTruncate(&p->testOut);
+    p->testErrcode = XJD1_OK;
+    p->shellFlags |= SHELL_TEST_MODE;
+  }
+  return 0;
+}
+
+/*
+** Return non-zero if string z matches glob pattern zGlob and zero if the
+** pattern does not match.
+**
+** Globbing rules:
+**
+**      '*'       Matches any sequence of zero or more characters.
+**
+**      '?'       Matches exactly one character.
+**
+**     [...]      Matches one character from the enclosed list of
+**                characters.
+**
+**     [^...]     Matches one character not in the enclosed list.
+**
+**      '#'       Matches any sequence of one or more digits with an
+**                optional + or - sign in front
+*/
+static int strnotglob(const char *zGlob, const char *z){
+  int c, c2;
+  int invert;
+  int seen;
+
+  while( (c = (*(zGlob++)))!=0 ){
+    if( c=='*' ){
+      while( (c=(*(zGlob++))) == '*' || c=='?' ){
+        if( c=='?' && (*(z++))==0 ) return 0;
+      }
+      if( c==0 ){
+        return 1;
+      }else if( c=='[' ){
+        while( *z && strnotglob(zGlob-1,z)==0 ){
+          z++;
+        }
+        return (*z)!=0;
+      }
+      while( (c2 = (*(z++)))!=0 ){
+        while( c2!=c ){
+          c2 = *(z++);
+          if( c2==0 ) return 0;
+        }
+        if( strnotglob(zGlob,z) ) return 1;
+      }
+      return 0;
+    }else if( c=='?' ){
+      if( (*(z++))==0 ) return 0;
+    }else if( c=='[' ){
+      int prior_c = 0;
+      seen = 0;
+      invert = 0;
+      c = *(z++);
+      if( c==0 ) return 0;
+      c2 = *(zGlob++);
+      if( c2=='^' ){
+        invert = 1;
+        c2 = *(zGlob++);
+      }
+      if( c2==']' ){
+        if( c==']' ) seen = 1;
+        c2 = *(zGlob++);
+      }
+      while( c2 && c2!=']' ){
+        if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
+          c2 = *(zGlob++);
+          if( c>=prior_c && c<=c2 ) seen = 1;
+          prior_c = 0;
+        }else{
+          if( c==c2 ){
+            seen = 1;
+          }
+          prior_c = c2;
+        }
+        c2 = *(zGlob++);
+      }
+      if( c2==0 || (seen ^ invert)==0 ) return 0;
+    }else if( c=='#' ){
+      if( (z[0]=='-' || z[0]=='+') && (z[1]>='0' && z[1]<='9') ) z++;
+      if( z[0]<'0' || z[0]>'9' ) return 0;
+      z++;
+      while( z[0]>='0' && z[0]<='9' ){ z++; }
+    }else{
+      if( c!=(*(z++)) ) return 0;
+    }
+  }
+  return *z==0;
+}
+static int strglob(const char *zGlob, const char *z){
+  return !strnotglob(zGlob,z);
+}
+
+/*
+** Verify the output from a test.
+*/
+static int shellResultCheck(
+  Shell *p,
+  int argc,
+  char **argv,
+  int(*xCmp)(const char*,const char*)
+){
+  const char *zAns = argc>=2 ? argv[1] : "";
+  const char *zRes = xjd1StringText(&p->testOut);
+  if( zRes==0 ) zRes = "";
+  p->nTest++;
+  if( xCmp(zAns, zRes) ){
+    p->nErr++;
+    fprintf(stderr, " Test failed: %s.%s\n Expected: [%s]\n      Got: [%s]\n",
+      p->zFile, p->zTestCase, argv[1], zRes);
+  }
+  return 0;
+}
+
+/* Command:  .result PATTERN */
+static int shellResult(Shell *p, int argc, char **argv){
+  shellResultCheck(p,argc,argv,strcmp);
+  return 0;
+}
+/* Command:  .glob PATTERN */
+static int shellGlob(Shell *p, int argc, char **argv){
+  shellResultCheck(p,argc,argv,strglob);
+  return 0;
+}
+/* Command:  .notglob PATTERN */
+static int shellNotGlob(Shell *p, int argc, char **argv){
+  shellResultCheck(p,argc,argv,strnotglob);
+  return 0;
+}
+/* Command:  .json PATTERN */
+static int shellJson(Shell *p, int argc, char **argv){
+  char *aArg[2];
+  String tidy;
+
+  assert( argc==1 || argc==2 );
+
+  xjd1StringInit(&tidy, 0, 0);
+  if( argc==2 ){
+    int rc = xjd1JsonTidy(&tidy, argv[1]);
+    assert( rc==XJD1_OK );
+  }
+
+  aArg[0] = argv[0];
+  aArg[1] = tidy.zBuf;
+  shellResultCheck(p, argc, aArg, strcmp);
+  xjd1StringClear(&tidy);
+
+  return 0;
+}
+
+/*
+** Command:  .open FILENAME
+*/
+static int shellOpenDB(Shell *p, int argc, char **argv){
+  if( argc>=2 ){
+    int rc;
+    if( p->pDb ) xjd1_close(p->pDb);
+    rc = xjd1_open(0, argv[1], &p->pDb);
+    if( rc!=XJD1_OK ){
+      fprintf(stderr, "%s:%d: cannot open \"%s\"\n",
+              p->zFile, p->nLine, argv[1]);
+      p->nErr++;
+      p->pDb = 0;
+    }
+  }    
+  return 0;
+}
+/*
+** Command:  .new FILENAME
+*/
+static int shellNewDB(Shell *p, int argc, char **argv){
+  if( argc>=2 ){
+    if( p->pDb ) xjd1_close(p->pDb);
+    p->pDb = 0;
+    unlink(argv[1]);
+    shellOpenDB(p, argc, argv);
+  }
+  return 0;
+}
+
+/* Forward declaration */
+static void processOneFile(Shell*, const char*);
+
+/*
+** Command:  .read FILENAME
+** Read and process text from a file.
+*/
+static int shellRead(Shell *p, int argc, char **argv){
+  if( argc>=2 ){
+    int nErr = p->nErr;
+    String newFile;
+    char *zNew;
+    xjd1StringInit(&newFile, 0, 0);
+    if( argv[1][0]=='/' || strcmp(p->zFile,"-")==0 ){
+      xjd1StringAppend(&newFile, argv[1], -1);
+    }else{
+      int i, j;
+      for(i=j=0; p->zFile[i]; i++){ if( p->zFile[i]=='/' ) j = i; }
+      xjd1StringAppendF(&newFile, "%.*s/%s", j, p->zFile, argv[1]);
+    }
+    zNew = xjd1StringText(&newFile);
+    printf("BEGIN %s\n", zNew);
+    processOneFile(p, zNew);
+    printf("END %s - %d new errors\n", zNew, p->nErr - nErr);
+    xjd1StringClear(&newFile);
+  }
+  return 0;
+}
+
+/*
+** Command:  .breakpoint
+** A place to seet a breakpoint
+*/
+static int shellBreakpoint(Shell *p, int argc, char **argv){
+  /* no-op */
+  return 0;
+}
+
+/*
+** Command:  .puts TEXT
+*/
+static int shellPuts(Shell *p, int argc, char **argv){
+  if( argc>=2 ) printf("%s\n", argv[1]);
+  return 0;
+}
+
+static void checkForTestError(Shell *p){
+  if( (p->shellFlags & SHELL_TEST_MODE) && p->testErrcode ){
+    fprintf(stderr, "%s:%d: ERROR: %s\n", p->zFile, p->nLine, p->testOut.zBuf);
+    xjd1StringTruncate(&p->testOut);
+    p->testErrcode = XJD1_OK;
+    p->nErr++;
+  }
+}
+
+/*
+** Process a command intended for this shell - not for the database.
+**
+** Return 1 to abort or 0 to continue.
+*/
+static int processMetaCommand(Shell *p){ char *z;
+  int n;
+  char *azArg[2];
+  int nArg;
+  int i;
+  int rc;
+  static const struct shcmd {
+    const char *zCmd;                   /* Name of the command */
+    int (*xCmd)(Shell*,int,char**);     /* Procedure to run the command */
+    const char *zHelp;                  /* Help string */
+  } cmds[] = {
+    { "quit",       shellQuit,        ".quit"               },
+    { "testcase",   shellTestcase,    ".testcase NAME"      },
+    { "json",       shellJson,        ".json TEXT"          },
+    { "result",     shellResult,      ".result TEXT"        },
+    { "glob",       shellGlob,        ".glob PATTERN"       },
+    { "notglob",    shellNotGlob,     ".notglob PATTERN"    },
+    { "puts",       shellPuts,        ".puts TEXT"          },
+    { "read",       shellRead,        ".read FILENAME"      },
+    { "open",       shellOpenDB,      ".open DATABASE"      },
+    { "new",        shellNewDB,       ".new DATABASE"       },
+    { "set",        shellSet,         ".set FLAG"           },
+    { "clear",      shellClear,       ".clear FLAG"         },
+    { "breakpoint", shellBreakpoint,  ".breakpoint"         },
+  };
+
+  /* Remove trailing whitespace from the command */
+  z = xjd1StringText(&p->inBuf);
+  if( p->shellFlags & SHELL_ECHO ){
+    fprintf(stdout, "%s", z);
+  }
+  n = xjd1StringLen(&p->inBuf);
+  while( n>0 && shellIsSpace(z[n-1]) ){ n--; }
+  z[n] = 0;
+
+  /* If the last character of the command is a backslash, the command 
+  ** arguments continue onto the next line. Return early without truncating
+  ** the input buffer in this case. */
+  if( z[n-1]=='\\' ){
+    p->inBuf.nUsed = n;
+    z[n-1] = ' ';
+    return 0;
+  }
+
+  /* Find the command name text and its argument (if any) */
+  z++;
+  azArg[0] = z;
+  for(i=0; z[i] && !shellIsSpace(z[i]); i++){}
+  if( z[i] ){
+    z[i] = 0;
+    z += i+1;
+    while( shellIsSpace(z[0]) ) z++;
+    azArg[1] = z;
+    nArg = 2;
+  }else{
+    azArg[1] = 0;
+    nArg = 1;
+  }
+
+  if( strcmp(azArg[0], "error") ){
+    checkForTestError(p);
+  }else{
+    p->testErrcode = XJD1_OK;
+    azArg[0] = "result";
+  }
+
+  /* Find the command */
+  for(i=0; i<sizeof(cmds)/sizeof(cmds[0]); i++){
+    if( strcmp(azArg[0], cmds[i].zCmd)==0 ){
+      rc = cmds[i].xCmd(p, nArg, azArg);
+      break;
+    }
+  }
+  if( i>=sizeof(cmds)/sizeof(cmds[0]) ){
+    p->nErr++;
+    fprintf(stderr, "%s:%d: unknown command \".%s\"\n", 
+            p->zFile, p->nLine, azArg[0]);
+    return 1;
+  }
+  xjd1StringTruncate(&p->inBuf);
+  return rc;
+}
+
+/*
+** Append text to the end of the testOut string.  If there is already
+** text in the testOut buffer, prepent a space first.
+*/
+static void appendTestOut(Shell *p, const char *z, int n){
+  if( xjd1StringLen(&p->testOut)>0 ){
+    xjd1StringAppend(&p->testOut, " ", 1);
+  }
+  xjd1StringAppend(&p->testOut, z, n);
+}
+
+/*
+** Run a single statment.
+*/
+static void processOneStatement(Shell *p, const char *zCmd){
+  xjd1_stmt *pStmt;
+  int N, rc;
+  int once = 0;
+  if( p->shellFlags & SHELL_ECHO ){
+    fprintf(stdout, "%s\n", zCmd);
+  }
+  xjd1_config(p->pDb, XJD1_CONFIG_PARSERTRACE, 
+              (p->shellFlags & SHELL_PARSER_TRACE)!=0);
+  rc = xjd1_stmt_new(p->pDb, zCmd, &pStmt, &N);
+  if( rc==XJD1_OK ){
+    if( p->shellFlags & SHELL_CMD_TRACE ){
+      char *zTrace = xjd1_stmt_debug_listing(pStmt);
+      if( zTrace ) printf("%s", zTrace);
+      free(zTrace);
+    }
+    do{
+      rc = xjd1_stmt_step(pStmt);
+      if( rc==XJD1_ROW ){
+        const char *zValue;
+        xjd1_stmt_value(pStmt, &zValue);
+        if( (p->shellFlags & SHELL_TEST_MODE)==0 ){
+          if( once==0 && (p->shellFlags & SHELL_ECHO)!=0 ){
+            printf("--------- query results ---------\n");
+            once = 1;
+          }
+          printf("%s\n", zValue);
+        }else{
+          appendTestOut(p, zValue, -1);
+        }
+      }
+    }while( rc==XJD1_ROW );
+    xjd1_stmt_delete(pStmt);
+  }else{
+    if( p->shellFlags & SHELL_TEST_MODE ){
+      appendTestOut(p, xjd1_errcode_name(p->pDb), -1);
+      appendTestOut(p, xjd1_errmsg(p->pDb), -1);
+      p->testErrcode = rc;
+    }else{
+      fprintf(stderr, "%s:%d: ERROR: %s\n",
+              p->zFile, p->nLine, xjd1_errmsg(p->pDb));
+      p->nErr++;
+    }
+  }
+  if( once ) printf("---------------------------------\n");
+}
+
+/*
+** Process one or more database commands
+*/
+static void processScript(Shell *p){
+  char *z, c;
+  int i;
+
+  checkForTestError(p);
+
+  if( p->pDb==0 ){
+    if( p->shellFlags & SHELL_ECHO ) printf("%s", xjd1StringText(&p->inBuf));
+    xjd1StringTruncate(&p->inBuf);
+    return;
+  }
+  while( xjd1StringLen(&p->inBuf) ){
+    z = xjd1StringText(&p->inBuf);
+    for(i=0; shellIsSpace(z[i]); i++){}
+    if( z[i]=='-' && z[i+1]=='-' ){
+      for(i+=2; z[i] && z[i]!='\n'; i++){}
+      if( p->shellFlags & SHELL_ECHO ){
+        printf("%.*s\n", i, z);
+      }
+      while( shellIsSpace(z[i]) ) i++;
+      xjd1StringRemovePrefix(&p->inBuf, i);
+      break;
+    }
+    if( z[i]==0 ){
+      xjd1StringTruncate(&p->inBuf);
+      break;
+    }
+    for(; z[i]; i++){
+      if( z[i]!=';' ) continue;
+      c = z[i+1];
+      z[i+1] = 0;
+      if( !xjd1_complete(z) ){
+        z[i+1] = c;
+        continue;
+      }
+      processOneStatement(p, z);
+      z[i+1] = c;
+      while( shellIsSpace(z[i+1]) ){ i++; }
+      xjd1StringRemovePrefix(&p->inBuf, i+1);
+      i = 0;
+      break;
+    }
+    if( i>0 ) break;
+  }
+}
+
+/*
+** Process a single file of input by reading the text of the file and
+** sending statements into the XJD1 database connection.
+*/
+static void processOneFile(
+  Shell *p,               /* Current state of the shell */
+  const char *zFile       /* Name of file to process.  "-" for stdin. */
+){
+  Shell savedState;
+  char *zIn;
+  char zLine[1000];
+
+  savedState = *p;
+  if( strcmp(zFile, "-")==0 ){
+    p->pIn = stdin;
+    p->isTTY = 1;
+  }else{
+    p->pIn = fopen(zFile, "r");
+    if( p->pIn==0 ){
+      fprintf(stderr, "%s:%d: cannot open \"%s\"\n", p->zFile, p->nLine, zFile);
+      p->nErr++;
+      return;
+    }
+    p->isTTY = 0;
+  }
+  p->zFile = zFile;
+  p->nLine = 0;
+  while( !feof(p->pIn) ){
+    if( p->isTTY ){
+      printf("%s", xjd1StringLen(&p->inBuf)>0 ? " ...> " : "xjd1> ");
+    }
+    if( fgets(zLine, sizeof(zLine), p->pIn)==0 ) break;
+    p->nLine++;
+    if( zLine[0]=='.' && xjd1StringLen(&p->inBuf) ){
+      fprintf(stderr, "%s:%d: missing ';'\n", p->zFile, p->nLine);
+      xjd1StringTruncate(&p->inBuf);
+    }
+    xjd1StringAppend(&p->inBuf, zLine, -1);
+    zIn = xjd1StringText(&p->inBuf);
+    if( zIn[0]=='.' ){
+      if( processMetaCommand(p) ) break;
+    }else{
+      processScript(p);
+    }
+  }
+  if( p->isTTY==0 ){
+    fclose(p->pIn);
+  }
+  p->zFile = savedState.zFile;
+  p->nLine = savedState.nLine;
+  p->pIn = savedState.pIn;
+  p->isTTY = savedState.isTTY;
+}
+
+int main(int argc, char **argv){
+  int i;
+  Shell s;
+
+  memset(&s, 0, sizeof(s));
+  if( argc>1 ){
+    for(i=1; i<argc; i++){
+      processOneFile(&s, argv[i]);
+    }
+  }else{
+    processOneFile(&s, "-");
+  }
+  if( s.nTest ){
+    printf("%d errors from %d tests\n", s.nErr, s.nTest);
+  }
+  if( s.pDb ) xjd1_close(s.pDb);
+  xjd1StringClear(&s.inBuf);
+  xjd1StringClear(&s.testOut);
+  return 0;
+}

+ 322 - 0
unql/src/stmt.c

@@ -0,0 +1,322 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Interfaces to prepared statment objects.
+*/
+#include "xjd1Int.h"
+
+/*
+** Create a new prepared statement for database connection pConn.  The
+** program code to be parsed is zStmt.  Return the new statement in *ppNew.
+** Write the number of zStmt bytes read into *pN.
+*/
+int xjd1_stmt_new(xjd1 *pConn, const char *zStmt, xjd1_stmt **ppNew, int *pN){
+  xjd1_stmt *p;
+  int dummy;
+  Command *pCmd;
+  int rc;
+
+  if( pN==0 ) pN = &dummy;
+  *pN = strlen(zStmt);
+  *ppNew = p = xjd1_malloc( sizeof(*p) );
+  if( p==0 ) return XJD1_NOMEM;
+  memset(p, 0, sizeof(*p));
+  p->pConn = pConn;
+  p->pNext = pConn->pStmt;
+  pConn->pStmt = p;
+  p->zCode = xjd1PoolDup(&p->sPool, zStmt, -1);
+  xjd1StringInit(&p->retValue, &p->sPool, 0);
+  xjd1StringInit(&p->errMsg, &p->sPool, 0);
+
+  rc = xjd1RunParser(pConn, p, p->zCode, pN);
+  pCmd = p->pCmd;
+  assert( rc==XJD1_OK || (pCmd==0 && pConn->errCode==rc) );
+
+  if( pCmd ){
+    switch( pCmd->eCmdType ){
+      case TK_SELECT: {
+        rc = xjd1QueryInit(pCmd->u.q.pQuery, p, 0);
+        break;
+      }
+      case TK_INSERT: {
+        xjd1QueryInit(pCmd->u.ins.pQuery, p, 0);
+        break;
+      }
+      case TK_DELETE: {
+        xjd1ExprInit(pCmd->u.del.pWhere, p, 0, 0, 0);
+        break;
+      }
+      case TK_UPDATE: {
+        xjd1ExprInit(pCmd->u.update.pWhere, p, 0, 0, 0);
+        xjd1ExprListInit(pCmd->u.update.pChng, p, 0, 0, 0);
+        xjd1ExprInit(pCmd->u.update.pUpsert, p, 0, 0, 0);
+        break;
+      }
+    }
+
+    if( p->errCode ){
+      xjd1Error(pConn, p->errCode, "%s", p->errMsg.zBuf);
+      rc = p->errCode;
+    }else if( rc!=XJD1_OK ){
+      xjd1Error(pConn, rc, 0);
+    }
+  }
+
+  if( rc!=XJD1_OK ){
+    xjd1_stmt_delete(p);
+    *ppNew = 0;
+  }
+  return rc;
+}
+
+/*
+** Configure a prepared statement.
+*/
+int xdj1_stmt_config(xjd1_stmt *pStmt, int op, ...){
+  return XJD1_UNKNOWN;
+}
+
+/*
+** Delete a prepared statement.
+*/
+int xjd1_stmt_delete(xjd1_stmt *pStmt){
+  Command *pCmd;
+  if( pStmt==0 ) return XJD1_OK;
+  pStmt->isDying = 1;
+  if( pStmt->nRef>0 ) return XJD1_OK;
+
+  pCmd = pStmt->pCmd;
+  if( pCmd ){
+    switch( pCmd->eCmdType ){
+      case TK_SELECT: {
+        xjd1QueryClose(pCmd->u.q.pQuery);
+        break;
+      }
+      case TK_INSERT: {
+        xjd1QueryClose(pCmd->u.ins.pQuery);
+        break;
+      }
+      case TK_DELETE: {
+        xjd1ExprClose(pCmd->u.del.pWhere);
+        break;
+      }
+      case TK_UPDATE: {
+        xjd1ExprClose(pCmd->u.update.pWhere);
+        xjd1ExprListClose(pCmd->u.update.pChng);
+        xjd1ExprClose(pCmd->u.update.pUpsert);
+        break;
+      }
+    }
+  }
+
+  if( pStmt->pPrev ){
+    pStmt->pPrev->pNext = pStmt->pNext;
+  }else{
+    //assert( pStmt->pConn->pStmt==pStmt ); wrong assumption
+    pStmt->pConn->pStmt = pStmt->pNext;
+  }
+  if( pStmt->pNext ){
+    pStmt->pNext->pPrev = pStmt->pPrev;
+  }
+  xjd1Unref(pStmt->pConn);
+  xjd1PoolClear(&pStmt->sPool);
+  xjd1StringClear(&pStmt->retValue);
+  xjd1_free(pStmt);
+  return XJD1_OK;
+}
+
+/*
+** Execute a prepared statement up to its next return value or until
+** it completes.
+*/
+int xjd1_stmt_step(xjd1_stmt *pStmt){
+  Command *pCmd;
+  int rc = XJD1_DONE;
+  if( pStmt==0 ) return rc;
+  pCmd = pStmt->pCmd;
+  if( pCmd==0 ) return rc;
+  switch( pCmd->eCmdType ){
+    case TK_CREATECOLLECTION: {
+      char *zSql;
+      int res;
+      char *zErr = 0;
+      zSql = sqlite3_mprintf("CREATE TABLE %s \"%w\"(x)",
+                 pCmd->u.crtab.ifExists ? "IF NOT EXISTS" : "",
+                 pCmd->u.crtab.zName);
+      res = sqlite3_exec(pStmt->pConn->db, zSql, 0, 0, &zErr);
+      if( zErr ){
+        xjd1Error(pStmt->pConn, XJD1_ERROR, "%s", zErr);
+        sqlite3_free(zErr);
+        rc = XJD1_ERROR;
+      }
+      sqlite3_free(zSql);
+      break;
+    }
+    case TK_DROPCOLLECTION: {
+      char *zSql;
+      int res;
+      char *zErr = 0;
+      zSql = sqlite3_mprintf("DROP TABLE %s \"%w\"",
+                 pCmd->u.crtab.ifExists ? "IF EXISTS" : "",
+                 pCmd->u.crtab.zName);
+      res = sqlite3_exec(pStmt->pConn->db, zSql, 0, 0, &zErr);
+      if( zErr ){
+        xjd1Error(pStmt->pConn, XJD1_ERROR, "%s", zErr);
+        sqlite3_free(zErr);
+        rc = XJD1_ERROR;
+      }
+      sqlite3_free(zSql);
+      break;
+    }
+    case TK_INSERT: {
+      JsonNode *pNode;
+      String json;
+      int res;
+      char *zErr;
+      char *zSql;
+      if( pCmd->u.ins.pQuery ){
+        xjd1Error(pStmt->pConn, XJD1_ERROR, 
+                 "INSERT INTO ... SELECT not yet implemented");
+        break;
+      }
+      pNode = xjd1ExprEval(pCmd->u.ins.pValue);
+      if( pNode==0 ) break;
+      xjd1StringInit(&json,0,0);
+      xjd1JsonRender(&json, pNode);
+      xjd1JsonFree(pNode);
+      zSql = sqlite3_mprintf("INSERT INTO \"%w\" VALUES('%q')",
+                  pCmd->u.ins.zName,
+                  xjd1StringText(&json));
+      xjd1StringClear(&json);
+      res = sqlite3_exec(pStmt->pConn->db, zSql, 0, 0, &zErr);
+      if( zErr ){
+        xjd1Error(pStmt->pConn, XJD1_ERROR, "%s", zErr);
+        sqlite3_free(zErr);
+        rc = XJD1_ERROR;
+      }
+      sqlite3_free(zSql);
+      break;
+    }
+    case TK_SELECT: {
+      Query *pQuery = pCmd->u.q.pQuery;
+      rc = xjd1QueryStep(pQuery);
+      xjd1StringClear(&pStmt->retValue);
+      if( rc==XJD1_ROW ){
+        JsonNode *pValue = xjd1QueryDoc(pQuery, 0);
+        xjd1JsonRender(&pStmt->retValue, pValue);
+        xjd1JsonFree(pValue);
+        pStmt->okValue = 1;
+      }else{
+        pStmt->okValue = 0;
+      }
+      break;
+    }
+    case TK_DELETE: {
+      rc = xjd1DeleteStep(pStmt);
+      break;
+    }
+    case TK_UPDATE: {
+      rc = xjd1UpdateStep(pStmt);
+      break;
+    }
+    case TK_PRAGMA: {
+      rc = xjd1PragmaStep(pStmt);
+      break;
+    }
+  }
+  return rc;
+}
+
+/*
+** Rewind a prepared statement back to the beginning.
+*/
+int xjd1_stmt_rewind(xjd1_stmt *pStmt){
+  Command *pCmd = pStmt->pCmd;
+  if( pCmd ){
+    switch( pCmd->eCmdType ){
+      case TK_SELECT: {
+        xjd1QueryRewind(pCmd->u.q.pQuery);
+        xjd1StringTruncate(&pStmt->retValue);
+        pStmt->okValue = 0;
+        break;
+      }
+      case TK_INSERT: {
+        xjd1QueryRewind(pCmd->u.ins.pQuery);
+        break;
+      }
+    }
+  }
+  return XJD1_OK;
+}
+
+/*
+** Return the output value of a prepared statement resulting from
+** its most recent xjd1_stmt_step() call.
+*/
+int xjd1_stmt_value(xjd1_stmt *pStmt, const char **pzValue){
+  if( pStmt==0 ) return XJD1_MISUSE;
+  *pzValue = pStmt->retValue.zBuf;
+  return XJD1_OK;
+}
+
+/*
+** Construct a human-readable listing of a prepared statement showing
+** its internal structure.  Used for debugging and analysis only.
+**
+** The return string is obtained from xjd1_malloc and must be freed by
+** the caller.
+*/
+char *xjd1_stmt_debug_listing(xjd1_stmt *p){
+  String x;
+  xjd1StringInit(&x, 0, 0);
+  xjd1TraceCommand(&x, 0, p->pCmd);
+  return x.zBuf;
+}
+
+/*
+** Return the current value for a particular document in the given
+** statement.
+**
+** The caller is responsible for invoking xjd1JsonFree() on the result.
+*/
+JsonNode *xjd1StmtDoc(xjd1_stmt *pStmt){
+  Command *pCmd;
+  JsonNode *pRes = 0;
+  if( pStmt==0 ) return 0;
+  pCmd = pStmt->pCmd;
+  if( pCmd==0 ) return 0;
+  switch( pCmd->eCmdType ){
+    case TK_SELECT: {
+      pRes = xjd1QueryDoc(pCmd->u.q.pQuery, 0);
+      break;
+    }
+    case TK_UPDATE:
+    case TK_DELETE: {
+      pRes = xjd1JsonRef(pStmt->pDoc);
+      break;
+    }
+  }
+  return pRes;
+}
+
+void xjd1StmtError(xjd1_stmt *pStmt, int errCode, const char *zFormat, ...){
+  va_list ap;
+  pStmt->errCode = errCode;
+  va_start(ap, zFormat);
+  xjd1StringVAppendF(&pStmt->errMsg, zFormat, ap);
+  va_end(ap);
+}
+

+ 762 - 0
unql/src/string.c

@@ -0,0 +1,762 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** String manipulation routines.
+*/
+#include "xjd1Int.h"
+
+/*
+** Compute a string length that is limited to what can be stored in
+** lower 30 bits of a 32-bit signed integer.
+**
+** The value returned will never be negative.  Nor will it ever be greater
+** than the actual length of the string.  For very long strings (greater
+** than 1GiB) the value returned might be less than the true string length.
+*/
+PRIVATE int xjd1Strlen30(const char *z){
+  const char *z2 = z;
+  if( z==0 ) return 0;
+  while( *z2 ){ z2++; }
+  return 0x3fffffff & (int)(z2 - z);
+}
+
+/*
+** Change the allocation size for a string to newSize bytes.  Return
+** 1 if an OOM error occurs or 0 on success.
+*/
+static int resizeString(String *pStr, int newSize){
+  char *zNew;
+  if( pStr->pPool ){
+    zNew = xjd1PoolMalloc(pStr->pPool, newSize);
+    if( zNew==0 ) return 1;
+    memcpy(zNew, pStr->zBuf, pStr->nUsed);
+  }else{
+    zNew = xjd1_realloc(pStr->zBuf, newSize);
+    if( zNew==0 ) return 1;
+  }
+  pStr->nAlloc = newSize;
+  pStr->zBuf = zNew;
+  return 0;
+}
+
+/*
+** Truncate a string to zero length
+*/
+void xjd1StringTruncate(String *pStr){
+  if( pStr ){
+    pStr->nUsed = 0;
+    if( pStr->zBuf ) pStr->zBuf[0] = 0;
+  }
+}
+
+/*
+** Return the value of a string.
+*/
+char *xjd1StringGet(String *pStr){
+  char *z;
+  if( pStr==0 ) return 0;
+  z = pStr->zBuf;
+  memset(pStr, 0, sizeof(*pStr));
+  return z;
+}
+
+/*
+** Append text in z to a string.  If n>=0 then append exactly
+** n bytes.  If n<0 then append all of z[] up to the zero terminator.
+**
+** Return the number of bytes appended.  0 is returned on an OOM
+** error.
+*/
+PRIVATE int xjd1StringAppend(String *pStr, const char *z, int n){
+  if( n<0 ) n = xjd1Strlen30(z);
+  if( pStr->nUsed + n + 1 >= pStr->nAlloc ){
+    if( resizeString(pStr, pStr->nAlloc*2 + n + 100) ) return 0;
+  }
+  if( z ){
+    memcpy(&pStr->zBuf[pStr->nUsed], z, n);
+    pStr->nUsed += n;
+    pStr->zBuf[pStr->nUsed] = 0;
+  }
+  return n;
+}
+
+/*
+** Initialize a string object that has been previously allocated.
+** In other words, turn bulk memory into a string object.
+*/
+PRIVATE void xjd1StringInit(String *pStr, Pool *pPool, int initSize){
+  memset(pStr, 0, sizeof(*pStr));
+  pStr->pPool = pPool;
+  if( initSize>0 ){
+    resizeString(pStr, initSize);
+  }
+}
+
+/*
+** Allocate a new string object.  Initialize the string buffer to
+** initSize bytes.  initSize can be zero.
+**
+** If pPool is not NULL then all string memory allocations including
+** the allocation of the String object itself, come from the identified
+** memory pool.  If pPool is NULL, then xjd1_malloc()/xjd1_free() are used.
+*/
+PRIVATE String *xjd1StringNew(Pool *pPool, int initSize){
+  String *pStr;
+  if( pPool ){
+    pStr = xjd1PoolMalloc(pPool, sizeof(*pStr));
+  }else{
+    pStr = xjd1_malloc( sizeof(*pStr) );
+  }
+  if( pStr) xjd1StringInit(pStr, pPool, initSize);
+  return pStr;
+}
+
+/*
+** Free the content of a string but not the String object itself.
+*/
+PRIVATE void xjd1StringClear(String *pStr){
+  if( pStr ){
+    if( pStr->pPool==0 ) xjd1_free(pStr->zBuf);
+    memset(pStr, 0, sizeof(*pStr));
+  }
+}
+
+/*
+** Free a string previously allocated using xjd1StringNew().
+*/
+PRIVATE void xjd1StringDelete(String *pStr){
+  if( pStr && pStr->pPool==0 ){
+    xjd1_free(pStr->zBuf);
+    xjd1_free(pStr);
+  }
+}
+
+/*
+** Consume the first N characters from the front of the string.
+*/
+PRIVATE void xjd1StringRemovePrefix(String *pStr, int N){
+  if( pStr==0 ) return;
+  if( pStr->nUsed<=N ){
+    xjd1StringTruncate(pStr);
+  }else{
+    memmove(pStr->zBuf, &pStr->zBuf[N], (pStr->nUsed+1)-N);
+    pStr->nUsed -= N;
+  }
+}
+
+/*
+** Conversion types fall into various categories as defined by the
+** following enumeration.
+*/
+#define etRADIX       1 /* Integer types.  %d, %x, %o, and so forth */
+#define etFLOAT       2 /* Floating point.  %f */
+#define etEXP         3 /* Exponentional notation. %e and %E */
+#define etGENERIC     4 /* Floating or exponential, depending on exponent. %g */
+#define etSIZE        5 /* Return number of characters processed so far. %n */
+#define etSTRING      6 /* Strings. %s */
+#define etDYNSTRING   7 /* Dynamically allocated strings. %z */
+#define etPERCENT     8 /* Percent symbol. %% */
+#define etCHARX       9 /* Characters. %c */
+#define etERROR      10 /* Used to indicate no such conversion type */
+#define etPOINTER    11 /* Pointer value.  %p */
+/* The rest are extensions, not normally found in printf() */
+#define etSTRESCAPE  12 /* Strings with escapes. %q */
+
+/*
+** An "etByte" is an 8-bit unsigned value.
+*/
+typedef unsigned char etByte;
+typedef long long int i64;
+typedef unsigned long long int u64;
+
+/*
+** Each builtin conversion character (ex: the 'd' in "%d") is described
+** by an instance of the following structure
+*/
+typedef struct et_info {   /* Information about each format field */
+  char fmttype;            /* The format field code letter */
+  etByte base;             /* The base for radix conversion */
+  etByte flags;            /* One or more of FLAG_ constants below */
+  etByte type;             /* Conversion paradigm */
+  etByte charset;          /* Offset into aDigits[] of the digits string */
+  etByte prefix;           /* Offset into aPrefix[] of the prefix string */
+} et_info;
+
+/*
+** Allowed values for et_info.flags
+*/
+#define FLAG_SIGNED  1     /* True if the value to convert is signed */
+#define FLAG_INTERN  2     /* True if for internal use only */
+#define FLAG_STRING  4     /* Allow infinity precision */
+
+
+/*
+** The following table is searched linearly, so it is good to put the
+** most frequently used conversion types first.
+*/
+static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
+static const char aPrefix[] = "-x0\000X0";
+static const et_info fmtinfo[] = {
+  {  'd', 10, 1, etRADIX,      0,  0 },
+  {  's',  0, 4, etSTRING,     0,  0 },
+  {  'g',  0, 1, etGENERIC,    30, 0 },
+  {  'q',  0, 4, etSTRESCAPE,  0,  0 },
+  {  'c',  0, 0, etCHARX,      0,  0 },
+  {  'o',  8, 0, etRADIX,      0,  2 },
+  {  'u', 10, 0, etRADIX,      0,  0 },
+  {  'x', 16, 0, etRADIX,      16, 1 },
+  {  'X', 16, 0, etRADIX,      0,  4 },
+  {  'f',  0, 1, etFLOAT,      0,  0 },
+  {  'e',  0, 1, etEXP,        30, 0 },
+  {  'E',  0, 1, etEXP,        14, 0 },
+  {  'G',  0, 1, etGENERIC,    14, 0 },
+  {  'i', 10, 1, etRADIX,      0,  0 },
+  {  'n',  0, 0, etSIZE,       0,  0 },
+  {  '%',  0, 0, etPERCENT,    0,  0 },
+  {  'p', 16, 0, etPOINTER,    0,  1 },
+};
+#define etNINFO  (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
+
+/*
+** "*val" is a double such that 0.1 <= *val < 10.0
+** Return the ascii code for the leading digit of *val, then
+** multiply "*val" by 10.0 to renormalize.
+**
+** Example:
+**     input:     *val = 3.14159
+**     output:    *val = 1.4159    function return = '3'
+**
+** The counter *cnt is incremented each time.  After counter exceeds
+** 16 (the number of significant digits in a 64-bit float) '0' is
+** always returned.
+*/
+static int et_getdigit(long double *val, int *cnt){
+  int digit;
+  long double d;
+  if( (*cnt)++ >= 16 ) return '0';
+  digit = (int)*val;
+  d = digit;
+  digit += '0';
+  *val = (*val - d)*10.0;
+  return digit;
+}
+
+/*
+** Size of temporary conversion buffer.
+*/
+#define etBUFSIZE 500
+
+/*
+** Find the length of a string as long as that length does not
+** exceed N bytes.  If no zero terminator is seen in the first
+** N bytes then return N.  If N is negative, then this routine
+** is an alias for strlen().
+*/
+static int StrNLen32(const char *z, int N){
+  int n = 0;
+  while( (N-- != 0) && *(z++)!=0 ){ n++; }
+  return n;
+}
+
+
+/*
+** INPUTS:
+**   pStr   Append output to this string.
+**
+**   fmt    This is the format string, as in the usual print.
+**
+**   ap     This is a pointer to a list of arguments.  Same as in
+**          vfprint.
+**
+** OUTPUTS:
+**          The return value is the total number of characters appended.
+**          Returns -1 on a error.
+**
+** Note that the order in which automatic variables are declared below
+** seems to make a big difference in determining how fast this beast
+** will run.
+*/
+PRIVATE int xjd1StringVAppendF(
+  String *pStr,                      /* Append output to this string */
+  const char *fmt,                   /* Format string */
+  va_list ap                         /* arguments */
+){
+  int c;                     /* Next character in the format string */
+  char *bufpt;               /* Pointer to the conversion buffer */
+  int precision;             /* Precision of the current field */
+  int length;                /* Length of the field */
+  int idx;                   /* A general purpose loop counter */
+  int count;                 /* Total number of characters output */
+  int width;                 /* Width of the current field */
+  etByte flag_leftjustify;   /* True if "-" flag is present */
+  etByte flag_plussign;      /* True if "+" flag is present */
+  etByte flag_blanksign;     /* True if " " flag is present */
+  etByte flag_alternateform; /* True if "#" flag is present */
+  etByte flag_altform2;      /* True if "!" flag is present */
+  etByte flag_zeropad;       /* True if field width constant starts with zero */
+  etByte flag_long;          /* True if "l" flag is present */
+  etByte flag_longlong;      /* True if the "ll" flag is present */
+  etByte done;               /* Loop termination flag */
+  u64 longvalue;             /* Value for integer types */
+  long double realvalue;     /* Value for real types */
+  const et_info *infop;      /* Pointer to the appropriate info structure */
+  char buf[etBUFSIZE];       /* Conversion buffer */
+  char prefix;               /* Prefix character.  "+" or "-" or " " or '\0'. */
+  etByte errorflag = 0;      /* True if an error is encountered */
+  etByte xtype;              /* Conversion paradigm */
+  char *zExtra;              /* Extra memory used for etTCLESCAPE conversions */
+  static const char spaces[] =
+   "                                                                         ";
+#define etSPACESIZE (sizeof(spaces)-1)
+  int  exp, e2;              /* exponent of real numbers */
+  double rounder;            /* Used for rounding floating point values */
+  etByte flag_dp;            /* True if decimal point should be shown */
+  etByte flag_rtz;           /* True if trailing zeros should be removed */
+  etByte flag_exp;           /* True to force display of the exponent */
+  int nsd;                   /* Number of significant digits returned */
+
+  count = length = 0;
+  bufpt = 0;
+  for(; (c=(*fmt))!=0; ++fmt){
+    if( c!='%' ){
+      int amt;
+      bufpt = (char *)fmt;
+      amt = 1;
+      while( (c=(*++fmt))!='%' && c!=0 ) amt++;
+      xjd1StringAppend(pStr,bufpt,amt);
+      count += amt;
+      if( c==0 ) break;
+    }
+    if( (c=(*++fmt))==0 ){
+      errorflag = 1;
+      xjd1StringAppend(pStr,"%",1);
+      count++;
+      break;
+    }
+    /* Find out what flags are present */
+    flag_leftjustify = flag_plussign = flag_blanksign = 
+     flag_alternateform = flag_altform2 = flag_zeropad = 0;
+    done = 0;
+    do{
+      switch( c ){
+        case '-':   flag_leftjustify = 1;     break;
+        case '+':   flag_plussign = 1;        break;
+        case ' ':   flag_blanksign = 1;       break;
+        case '#':   flag_alternateform = 1;   break;
+        case '!':   flag_altform2 = 1;        break;
+        case '0':   flag_zeropad = 1;         break;
+        default:    done = 1;                 break;
+      }
+    }while( !done && (c=(*++fmt))!=0 );
+    /* Get the field width */
+    width = 0;
+    if( c=='*' ){
+      width = va_arg(ap,int);
+      if( width<0 ){
+        flag_leftjustify = 1;
+        width = -width;
+      }
+      c = *++fmt;
+    }else{
+      while( c>='0' && c<='9' ){
+        width = width*10 + c - '0';
+        c = *++fmt;
+      }
+    }
+    if( width > etBUFSIZE-10 ){
+      width = etBUFSIZE-10;
+    }
+    /* Get the precision */
+    if( c=='.' ){
+      precision = 0;
+      c = *++fmt;
+      if( c=='*' ){
+        precision = va_arg(ap,int);
+        if( precision<0 ) precision = -precision;
+        c = *++fmt;
+      }else{
+        while( c>='0' && c<='9' ){
+          precision = precision*10 + c - '0';
+          c = *++fmt;
+        }
+      }
+    }else{
+      precision = -1;
+    }
+    /* Get the conversion type modifier */
+    if( c=='l' ){
+      flag_long = 1;
+      c = *++fmt;
+      if( c=='l' ){
+        flag_longlong = 1;
+        c = *++fmt;
+      }else{
+        flag_longlong = 0;
+      }
+    }else{
+      flag_long = flag_longlong = 0;
+    }
+    /* Fetch the info entry for the field */
+    infop = 0;
+    xtype = etERROR;
+    for(idx=0; idx<etNINFO; idx++){
+      if( c==fmtinfo[idx].fmttype ){
+        infop = &fmtinfo[idx];
+        xtype = infop->type;
+        break;
+      }
+    }
+    zExtra = 0;
+
+    /* Limit the precision to prevent overflowing buf[] during conversion */
+    if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
+      precision = etBUFSIZE-40;
+    }
+
+    /*
+    ** At this point, variables are initialized as follows:
+    **
+    **   flag_alternateform          TRUE if a '#' is present.
+    **   flag_altform2               TRUE if a '!' is present.
+    **   flag_plussign               TRUE if a '+' is present.
+    **   flag_leftjustify            TRUE if a '-' is present or if the
+    **                               field width was negative.
+    **   flag_zeropad                TRUE if the width began with 0.
+    **   flag_long                   TRUE if the letter 'l' (ell) prefixed
+    **                               the conversion character.
+    **   flag_longlong               TRUE if the letter 'll' (ell ell) prefixed
+    **                               the conversion character.
+    **   flag_blanksign              TRUE if a ' ' is present.
+    **   width                       The specified field width.  This is
+    **                               always non-negative.  Zero is the default.
+    **   precision                   The specified precision.  The default
+    **                               is -1.
+    **   xtype                       The class of the conversion.
+    **   infop                       Pointer to the appropriate info struct.
+    */
+    switch( xtype ){
+      case etPOINTER:
+        flag_longlong = sizeof(char*)==sizeof(i64);
+        flag_long = sizeof(char*)==sizeof(long int);
+        /* Fall through into the next case */
+      case etRADIX:
+        if( infop->flags & FLAG_SIGNED ){
+          i64 v;
+          if( flag_longlong )   v = va_arg(ap,i64);
+          else if( flag_long )  v = va_arg(ap,long int);
+          else                  v = va_arg(ap,int);
+          if( v<0 ){
+            longvalue = -v;
+            prefix = '-';
+          }else{
+            longvalue = v;
+            if( flag_plussign )        prefix = '+';
+            else if( flag_blanksign )  prefix = ' ';
+            else                       prefix = 0;
+          }
+        }else{
+          if( flag_longlong )   longvalue = va_arg(ap,u64);
+          else if( flag_long )  longvalue = va_arg(ap,unsigned long int);
+          else                  longvalue = va_arg(ap,unsigned int);
+          prefix = 0;
+        }
+        if( longvalue==0 ) flag_alternateform = 0;
+        if( flag_zeropad && precision<width-(prefix!=0) ){
+          precision = width-(prefix!=0);
+        }
+        bufpt = &buf[etBUFSIZE-1];
+        {
+          register const char *cset;      /* Use registers for speed */
+          register int base;
+          cset = &aDigits[infop->charset];
+          base = infop->base;
+          do{                                           /* Convert to ascii */
+            *(--bufpt) = cset[longvalue%base];
+            longvalue = longvalue/base;
+          }while( longvalue>0 );
+        }
+        length = &buf[etBUFSIZE-1]-bufpt;
+        for(idx=precision-length; idx>0; idx--){
+          *(--bufpt) = '0';                             /* Zero pad */
+        }
+        if( prefix ) *(--bufpt) = prefix;               /* Add sign */
+        if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
+          const char *pre;
+          char x;
+          pre = &aPrefix[infop->prefix];
+          if( *bufpt!=pre[0] ){
+            for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
+          }
+        }
+        length = &buf[etBUFSIZE-1]-bufpt;
+        break;
+      case etFLOAT:
+      case etEXP:
+      case etGENERIC:
+        realvalue = va_arg(ap,double);
+        if( precision<0 ) precision = 6;         /* Set default precision */
+        if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
+        if( realvalue<0.0 ){
+          realvalue = -realvalue;
+          prefix = '-';
+        }else{
+          if( flag_plussign )          prefix = '+';
+          else if( flag_blanksign )    prefix = ' ';
+          else                         prefix = 0;
+        }
+        if( xtype==etGENERIC && precision>0 ) precision--;
+#if 0
+        /* Rounding works like BSD when the constant 0.4999 is used.  Wierd! */
+        for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
+#else
+        /* It makes more sense to use 0.5 */
+        for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
+#endif
+        if( xtype==etFLOAT ) realvalue += rounder;
+        /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+        exp = 0;
+        if( realvalue>0.0 ){
+          while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
+          while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
+          while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+          while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
+          while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
+          if( exp>350 || exp<-350 ){
+            bufpt = "NaN";
+            length = 3;
+            break;
+          }
+        }
+        bufpt = buf;
+        /*
+        ** If the field type is etGENERIC, then convert to either etEXP
+        ** or etFLOAT, as appropriate.
+        */
+        flag_exp = xtype==etEXP;
+        if( xtype!=etFLOAT ){
+          realvalue += rounder;
+          if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+        }
+        if( xtype==etGENERIC ){
+          flag_rtz = !flag_alternateform;
+          if( exp<-4 || exp>precision ){
+            xtype = etEXP;
+          }else{
+            precision = precision - exp;
+            xtype = etFLOAT;
+          }
+        }else{
+          flag_rtz = 0;
+        }
+        if( xtype==etEXP ){
+          e2 = 0;
+        }else{
+          e2 = exp;
+        }
+        nsd = 0;
+        flag_dp = (precision>0) | flag_alternateform | flag_altform2;
+        /* The sign in front of the number */
+        if( prefix ){
+          *(bufpt++) = prefix;
+        }
+        /* Digits prior to the decimal point */
+        if( e2<0 ){
+          *(bufpt++) = '0';
+        }else{
+          for(; e2>=0; e2--){
+            *(bufpt++) = et_getdigit(&realvalue,&nsd);
+          }
+        }
+        /* The decimal point */
+        if( flag_dp ){
+          *(bufpt++) = '.';
+        }
+        /* "0" digits after the decimal point but before the first
+        ** significant digit of the number */
+        for(e2++; e2<0 && precision>0; precision--, e2++){
+          *(bufpt++) = '0';
+        }
+        /* Significant digits after the decimal point */
+        while( (precision--)>0 ){
+          *(bufpt++) = et_getdigit(&realvalue,&nsd);
+        }
+        /* Remove trailing zeros and the "." if no digits follow the "." */
+        if( flag_rtz && flag_dp ){
+          while( bufpt[-1]=='0' ) *(--bufpt) = 0;
+          assert( bufpt>buf );
+          if( bufpt[-1]=='.' ){
+            if( flag_altform2 ){
+              *(bufpt++) = '0';
+            }else{
+              *(--bufpt) = 0;
+            }
+          }
+        }
+        /* Add the "eNNN" suffix */
+        if( flag_exp || (xtype==etEXP && exp) ){
+          *(bufpt++) = aDigits[infop->charset];
+          if( exp<0 ){
+            *(bufpt++) = '-'; exp = -exp;
+          }else{
+            *(bufpt++) = '+';
+          }
+          if( exp>=100 ){
+            *(bufpt++) = (exp/100)+'0';                /* 100's digit */
+            exp %= 100;
+          }
+          *(bufpt++) = exp/10+'0';                     /* 10's digit */
+          *(bufpt++) = exp%10+'0';                     /* 1's digit */
+        }
+        *bufpt = 0;
+
+        /* The converted number is in buf[] and zero terminated. Output it.
+        ** Note that the number is in the usual order, not reversed as with
+        ** integer conversions. */
+        length = bufpt-buf;
+        bufpt = buf;
+
+        /* Special case:  Add leading zeros if the flag_zeropad flag is
+        ** set and we are not left justified */
+        if( flag_zeropad && !flag_leftjustify && length < width){
+          int i;
+          int nPad = width - length;
+          for(i=width; i>=nPad; i--){
+            bufpt[i] = bufpt[i-nPad];
+          }
+          i = prefix!=0;
+          while( nPad-- ) bufpt[i++] = '0';
+          length = width;
+        }
+        break;
+      case etSIZE:
+        *(va_arg(ap,int*)) = count;
+        length = width = 0;
+        break;
+      case etPERCENT:
+        buf[0] = '%';
+        bufpt = buf;
+        length = 1;
+        break;
+      case etCHARX:
+        c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
+        if( precision>=0 ){
+          for(idx=1; idx<precision; idx++) buf[idx] = c;
+          length = precision;
+        }else{
+          length =1;
+        }
+        bufpt = buf;
+        break;
+      case etSTRING: {
+        int limit = flag_alternateform ? va_arg(ap,int) : -1;
+        bufpt = va_arg(ap,char*);
+        if( bufpt==0 ){
+          bufpt = "";
+        }else if( xtype==etDYNSTRING ){
+          zExtra = bufpt;
+        }
+        length = StrNLen32(bufpt, limit);
+        if( precision>=0 && precision<length ) length = precision;
+        break;
+      }
+      case etSTRESCAPE: {
+        int i, j, n, ch, isnull;
+        int limit = flag_alternateform ? va_arg(ap,int) : -1;
+        char *escarg = va_arg(ap,char*);
+        isnull = escarg==0;
+        if( isnull ) escarg = "(NULL)";
+        if( limit<0 ) limit = strlen(escarg);
+        for(i=n=0; i<limit; i++){
+          if( escarg[i]=='"' || escarg[i]=='\\' )  n++;
+        }
+        n += i + 1;
+        if( n>etBUFSIZE ){
+          bufpt = zExtra = xjd1_malloc( n );
+        }else{
+          bufpt = buf;
+        }
+        j = 0;
+        for(i=0; i<limit; i++){
+          ch = escarg[i];
+          if( ch=='"' || ch=='\\' ) bufpt[j++] = '\\';
+          bufpt[j++] = ch;
+        }
+        bufpt[j] = 0;
+        length = j;
+        if( precision>=0 && precision<length ) length = precision;
+        break;
+      }
+      case etERROR:
+        buf[0] = '%';
+        buf[1] = c;
+        errorflag = 0;
+        idx = 1+(c!=0);
+        xjd1StringAppend(pStr,"%",idx);
+        count += idx;
+        if( c==0 ) fmt--;
+        break;
+    }/* End switch over the format type */
+    /*
+    ** The text of the conversion is pointed to by "bufpt" and is
+    ** "length" characters long.  The field width is "width".  Do
+    ** the output.
+    */
+    if( !flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        count += nspace;
+        while( nspace>=etSPACESIZE ){
+          xjd1StringAppend(pStr,spaces,etSPACESIZE);
+          nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ) xjd1StringAppend(pStr,spaces,nspace);
+      }
+    }
+    if( length>0 ){
+      xjd1StringAppend(pStr,bufpt,length);
+      count += length;
+    }
+    if( flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        count += nspace;
+        while( nspace>=etSPACESIZE ){
+          xjd1StringAppend(pStr,spaces,etSPACESIZE);
+          nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ) xjd1StringAppend(pStr,spaces,nspace);
+      }
+    }
+    if( zExtra ){
+      xjd1_free(zExtra);
+    }
+  }/* End for loop over the format string */
+  return errorflag ? -1 : count;
+} /* End of function */
+
+/*
+** Append formatted text to a string.  Return the number of bytes appended.
+*/
+PRIVATE int xjd1StringAppendF(String *pStr, const char *zFormat, ...){
+  va_list ap;
+  int rc;
+  va_start(ap, zFormat);
+  rc = xjd1StringVAppendF(pStr, zFormat, ap);
+  va_end(ap);
+  return rc;
+}

+ 496 - 0
unql/src/tokenize.c

@@ -0,0 +1,496 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** The tokenizer
+*/
+#include "xjd1Int.h"
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+/*
+** The following 256 byte lookup table is used to support built-in
+** equivalents to the following standard library functions:
+**
+**   isspace()                        0x01
+**   isalpha()                        0x02
+**   isdigit()                        0x04
+**   isalnum()                        0x06
+**   isxdigit()                       0x08
+**   identifier character             0x40
+**
+** Bit 0x20 is set if the mapped character requires translation to upper
+** case. i.e. if the character is a lower-case ASCII character.
+** If x is a lower-case ASCII character, then its upper-case equivalent
+** is (x - 0x20). Therefore toupper() can be implemented as:
+**
+**   (x & ~(map[x]&0x20))
+**
+** Bit 0x40 is set if the character non-alphanumeric and can be used in an
+** identifier.  Identifiers are alphanumerics, "_" and any
+** non-ASCII UTF character. Hence the test for whether or not a character is
+** part of an identifier is 0x46.
+*/
+const unsigned char xjd1CtypeMap[256] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 00..07    ........ */
+  0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,  /* 08..0f    ........ */
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 10..17    ........ */
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 18..1f    ........ */
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 20..27     !"#$%&' */
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 28..2f    ()*+,-./ */
+  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,  /* 30..37    01234567 */
+  0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 38..3f    89:;<=>? */
+
+  0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02,  /* 40..47    @ABCDEFG */
+  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,  /* 48..4f    HIJKLMNO */
+  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,  /* 50..57    PQRSTUVW */
+  0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40,  /* 58..5f    XYZ[\]^_ */
+  0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22,  /* 60..67    `abcdefg */
+  0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,  /* 68..6f    hijklmno */
+  0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,  /* 70..77    pqrstuvw */
+  0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,  /* 78..7f    xyz{|}~. */
+
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* 80..87    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* 88..8f    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* 90..97    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* 98..9f    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* a0..a7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* a8..af    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* b0..b7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* b8..bf    ........ */
+
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* c0..c7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* c8..cf    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* d0..d7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* d8..df    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* e0..e7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* e8..ef    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,  /* f0..f7    ........ */
+  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40   /* f8..ff    ........ */
+};
+
+/**********************************************************************
+** The following code is automatically generated
+** by ../tool/mkkeywordhash.c
+*/
+/* Hash score: 61 */
+static int keywordCode(const char *z, int n){
+  /* zText[] encodes 333 bytes of keywords in 224 bytes */
+  /*   BEGINTORDEROLLBACKELSELECTGROUPDATEACHAVINGLOBYWITHINSERTALL       */
+  /*   IMITASCENDINGASYNCHRONOUSCOLLATEXISTSCOLLECTIONULLCREATEXCEPT      */
+  /*   DELETEDESCENDINGDROPRAGMAFLATTENOTIFROMILIKEUNIONVALUEWHEREin      */
+  /*   ullCOMMITDISTINCTINTERSECTOFFSETfalsetrue                          */
+  static const char zText[223] = {
+    'B','E','G','I','N','T','O','R','D','E','R','O','L','L','B','A','C','K',
+    'E','L','S','E','L','E','C','T','G','R','O','U','P','D','A','T','E','A',
+    'C','H','A','V','I','N','G','L','O','B','Y','W','I','T','H','I','N','S',
+    'E','R','T','A','L','L','I','M','I','T','A','S','C','E','N','D','I','N',
+    'G','A','S','Y','N','C','H','R','O','N','O','U','S','C','O','L','L','A',
+    'T','E','X','I','S','T','S','C','O','L','L','E','C','T','I','O','N','U',
+    'L','L','C','R','E','A','T','E','X','C','E','P','T','D','E','L','E','T',
+    'E','D','E','S','C','E','N','D','I','N','G','D','R','O','P','R','A','G',
+    'M','A','F','L','A','T','T','E','N','O','T','I','F','R','O','M','I','L',
+    'I','K','E','U','N','I','O','N','V','A','L','U','E','W','H','E','R','E',
+    'i','n','u','l','l','C','O','M','M','I','T','D','I','S','T','I','N','C',
+    'T','I','N','T','E','R','S','E','C','T','O','F','F','S','E','T','f','a',
+    'l','s','e','t','r','u','e',
+  };
+  static const unsigned char aHash[97] = {
+       0,  34,   1,   0,   7,   0,  25,  26,   0,  39,   0,   0,  20,
+       0,  42,  37,  35,  46,  43,   0,   0,   0,  40,   0,   0,   8,
+      21,   0,   0,   4,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+       0,   0,   0,  44,   0,  13,   0,   0,  50,   0,   0,   6,   0,
+       0,   0,  45,  41,   0,  52,  22,   0,   0,   0,   0,   0,  24,
+      28,  49,  36,  19,  16,   0,   0,   0,   2,  17,  32,   0,  48,
+       0,   0,   0,  51,   0,   0,  27,  30,   0,   0,   0,  31,  14,
+       5,   0,   0,  23,  15,  47,
+  };
+  static const unsigned char aNext[52] = {
+       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+      11,   0,   0,   0,   0,   9,   0,   0,   0,   0,   0,   0,   0,
+      18,   0,   0,   0,   0,   3,   0,  12,   0,   0,   0,   0,  29,
+      38,   0,  10,   0,   0,   0,   0,  33,   0,   0,   0,   0,   0,
+  };
+  static const unsigned char aLen[52] = {
+       5,   4,   5,   8,   4,   6,   5,   6,   4,   6,   4,   2,   6,
+       6,   3,   5,   3,   9,   5,  12,   2,  11,   4,   7,   6,  10,
+       4,   6,   6,   6,   4,  10,   4,   6,   7,   3,   2,   4,   5,
+       4,   5,   5,   5,   2,   4,   6,   8,   9,   6,   3,   5,   4,
+  };
+  static const unsigned short int aOffset[52] = {
+       0,   3,   6,  10,  18,  20,  26,  29,  34,  37,  42,  45,  47,
+      51,  57,  59,  64,  64,  73,  73,  73,  74,  74,  85,  91,  97,
+     106, 110, 115, 121, 127, 127, 137, 140, 146, 152, 155, 156, 160,
+     161, 165, 170, 175, 180, 181, 185, 191, 199, 208, 211, 214, 219,
+  };
+  static const unsigned char aCode[52] = {
+    TK_BEGIN,      TK_INTO,       TK_ORDER,      TK_ROLLBACK,   TK_ELSE,       
+    TK_SELECT,     TK_GROUP,      TK_UPDATE,     TK_FLATTENOP,  TK_HAVING,     
+    TK_LIKEOP,     TK_BY,         TK_WITHIN,     TK_INSERT,     TK_ALL,        
+    TK_LIMIT,      TK_ASCENDING,  TK_ASCENDING,  TK_ASYNC,      TK_ASYNC,      
+    TK_AS,         TK_SYNC,       TK_SYNC,       TK_COLLATE,    TK_EXISTS,     
+    TK_COLLECTION, TK_NULL,       TK_CREATE,     TK_EXCEPT,     TK_DELETE,     
+    TK_DESCENDING, TK_DESCENDING, TK_DROP,       TK_PRAGMA,     TK_FLATTENOP,  
+    TK_NOT,        TK_IF,         TK_FROM,       TK_ILIKEOP,    TK_LIKEOP,     
+    TK_UNION,      TK_VALUE,      TK_WHERE,      TK_IN,         TK_NULL,       
+    TK_COMMIT,     TK_DISTINCT,   TK_INTERSECT,  TK_OFFSET,     TK_SET,        
+    TK_FALSE,      TK_TRUE,       
+  };
+  int h, i;
+  if( n<2 ) return TK_ID;
+  h = (z[0]*4 ^ z[n-1]*3 ^ n) % 97;
+  for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
+    if( aLen[i]==n && memcmp(&zText[aOffset[i]],z,n)==0 ){
+      return aCode[i];
+    }
+  }
+  return TK_ID;
+}
+#define XJD1_N_KEYWORD 52
+
+/* End of the automatically generated hash code
+*********************************************************************/
+
+/*
+** Return the length of the token that begins at z[0].
+** Store the token type in *tokenType before returning.
+*/
+int xjd1GetToken(const unsigned char *z, int *tokenType){
+  int i, c;
+  switch( *z ){
+    case ' ': case '\t': case '\n': case '\f': case '\r': {
+      for(i=1; xjd1Isspace(z[i]); i++){}
+      *tokenType = TK_SPACE;
+      return i;
+    }
+    case '-': {
+      if( z[1]=='-' ){
+        for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
+        *tokenType = TK_SPACE;
+        return i;
+      }
+      *tokenType = TK_MINUS;
+      return 1;
+    }
+    case '?': {
+      *tokenType = TK_QM;
+      return 1;
+    }
+    case '(': {
+      *tokenType = TK_LP;
+      return 1;
+    }
+    case ')': {
+      *tokenType = TK_RP;
+      return 1;
+    }
+    case '[': {
+      *tokenType = TK_LB;
+      return 1;
+    }
+    case ']': {
+      *tokenType = TK_RB;
+      return 1;
+    }
+    case '{': {
+      *tokenType = TK_LC;
+      return 1;
+    }
+    case '}': {
+      *tokenType = TK_RC;
+      return 1;
+    }
+    case ';': {
+      *tokenType = TK_SEMI;
+      return 1;
+    }
+    case '+': {
+      *tokenType = TK_PLUS;
+      return 1;
+    }
+    case '*': {
+      *tokenType = TK_STAR;
+      return 1;
+    }
+    case '/': {
+      if( z[1]!='*' || z[2]==0 ){
+        *tokenType = TK_SLASH;
+        return 1;
+      }
+      for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
+      if( c ) i++;
+      *tokenType = TK_SPACE;
+      return i;
+    }
+    case '%': {
+      *tokenType = TK_REM;
+      return 1;
+    }
+    case '=': {
+      if( z[1]!='=' ){
+        *tokenType = TK_EQ;
+        return 1;
+      }else if( z[2]=='=' ){
+        *tokenType = TK_EQ3;
+        return 3;
+      }else{
+        *tokenType = TK_EQEQ;
+        return 2;
+      }
+    }
+    case '<': {
+      if( (c=z[1])=='=' ){
+        *tokenType = TK_LE;
+        return 2;
+      }else if( c=='>' ){
+        *tokenType = TK_NE;
+        return 2;
+      }else if( c=='<' ){
+        *tokenType = TK_LSHIFT;
+        return 2;
+      }else{
+        *tokenType = TK_LT;
+        return 1;
+      }
+    }
+    case '>': {
+      if( (c=z[1])=='=' ){
+        *tokenType = TK_GE;
+        return 2;
+      }else if( c!='>' ){
+        *tokenType = TK_GT;
+        return 1;
+      }else if( z[2]=='>' ){
+        *tokenType = TK_URSHIFT;
+        return 3;
+      }else{
+        *tokenType = TK_RSHIFT;
+        return 2;
+      }
+    }
+    case '!': {
+      if( z[1]!='=' ){
+        *tokenType = TK_BANG;
+        return 1;
+      }else if( z[2]=='=' ){
+        *tokenType = TK_NE3;
+        return 3;
+      }else{
+        *tokenType = TK_NE;
+        return 2;
+      }
+    }
+    case '|': {
+      if( z[1]!='|' ){
+        *tokenType = TK_BITOR;
+        return 1;
+      }else{
+        *tokenType = TK_OR;
+        return 2;
+      }
+    }
+    case ',': {
+      *tokenType = TK_COMMA;
+      return 1;
+    }
+    case ':': {
+      *tokenType = TK_COLON;
+      return 1;
+    }
+    case '&': {
+      if( z[1]=='&' ){
+        *tokenType = TK_AND;
+        return 2;
+      }else{
+        *tokenType = TK_BITAND;
+        return 1;
+      }
+    }
+    case '~': {
+      *tokenType = TK_BITNOT;
+      return 1;
+    }
+    case '"': {
+      int delim = z[0];
+      for(i=1; (c=z[i])!=0; ++i){
+        if(c == '\\'){
+            switch(z[i+1])
+            {
+            case '\\':
+            case '"':
+            case 'n':
+            case 'r':
+            case 'f':
+            case 't':
+            case 'b':
+                    ++i;
+                break;
+            }
+        }
+        if( c==delim ) {
+            break;
+        }
+      }
+      *tokenType = TK_STRING;
+      return i+1;
+    }
+    case '.': {
+      if( !xjd1Isdigit(z[1]) ){
+        *tokenType = TK_DOT;
+        return 1;
+      }
+      /* If the next character is a digit, this is a floating point
+      ** number that begins with ".".  Fall thru into the next case */
+    }
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9': {
+      *tokenType = TK_INTEGER;
+      for(i=0; xjd1Isdigit(z[i]); i++){}
+      if( z[i]=='.' ){
+        i++;
+        while( xjd1Isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }
+      if( (z[i]=='e' || z[i]=='E') &&
+           ( xjd1Isdigit(z[i+1])
+            || ((z[i+1]=='+' || z[i+1]=='-') && xjd1Isdigit(z[i+2]))
+           )
+      ){
+        i += 2;
+        while( xjd1Isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }
+      while( xjd1Isident(z[i]) ){
+        *tokenType = TK_ILLEGAL;
+        i++;
+      }
+      return i;
+    }
+    default: {
+      if( !xjd1Isident(*z) ){
+        break;
+      }
+      for(i=1; xjd1Isident(z[i]); i++){}
+      *tokenType = keywordCode((char*)z, i);
+      return i;
+    }
+  }
+  *tokenType = TK_ILLEGAL;
+  return 1;
+}
+
+static void *parserAlloc(size_t N){
+  return xjd1_malloc((int)N);
+}
+
+/*
+** Run the parser on the given SQL string.  The parser structure is
+** passed in.  An SQLITE_ status code is returned.  If an error occurs
+** then an and attempt is made to write an error message into
+** memory obtained from xjd1_malloc() and to make *pzErrMsg point to that
+** error message.
+*/
+int xjd1RunParser(
+  xjd1 *pConn,
+  xjd1_stmt *pStmt,
+  const char *zCode,
+  int *pN
+){
+  int i = 0;
+  Parse sParse;
+  int tokenType;
+  void *pEngine;
+  int lastTokenParsed = 0;
+  int nToken = 0;
+  extern void *xjd1ParserAlloc(void*(*)(size_t));
+  extern void xjd1ParserFree(void*, void(*)(void*));
+  extern void xjd1Parser(void*,int,Token,Parse*);
+
+#ifndef NDEBUG
+  if( pConn->parserTrace ){
+    extern void xjd1ParserTrace(FILE*, char*);
+    xjd1ParserTrace(stdout, "parser> ");
+  }
+#endif
+
+  pEngine = xjd1ParserAlloc(parserAlloc);
+  memset(&sParse, 0, sizeof(sParse));
+  if( pEngine==0 ){
+    sParse.errCode = XJD1_NOMEM;
+    goto abort_parse;
+  }
+
+  sParse.pConn = pConn;
+  sParse.pPool = &pStmt->sPool;
+  xjd1StringInit(&sParse.errMsg, &pStmt->sPool, 0);
+  while( zCode[i] && sParse.errCode==0 ){
+    assert( i>=0 );
+    sParse.sTok.z = &zCode[i];
+    sParse.sTok.n = xjd1GetToken((unsigned char*)&zCode[i], &tokenType);
+    i += sParse.sTok.n;
+    switch( tokenType ){
+      case TK_SPACE: {
+        break;
+      }
+      case TK_ILLEGAL: {
+        xjd1ParseError(&sParse, XJD1_ERROR,
+            "unrecognized token: \"%.*s\"", sParse.sTok.n, sParse.sTok.z
+        );
+        goto abort_parse;
+      }
+      default: {
+        nToken++;
+        xjd1Parser(pEngine, tokenType, sParse.sTok, &sParse);
+        lastTokenParsed = tokenType;
+        if( sParse.errCode || tokenType==TK_SEMI ) goto abort_parse;
+        break;
+      }
+    }
+  }
+
+abort_parse:
+  if( sParse.errCode==XJD1_OK && nToken>0 ){
+    if( lastTokenParsed!=TK_SEMI ){
+      sParse.sTok.z = ";";
+      sParse.sTok.n = 1;
+      xjd1Parser(pEngine, TK_SEMI, sParse.sTok, &sParse);
+    }
+    xjd1Parser(pEngine, 0, sParse.sTok, &sParse);
+  }
+  xjd1ParserFree(pEngine, xjd1_free);
+
+  if( sParse.errCode!=XJD1_OK ){
+    xjd1Error(pConn, sParse.errCode, "%s", sParse.errMsg.zBuf);
+  }
+  pStmt->pCmd = sParse.pCmd;
+  *pN = i;
+
+  return sParse.errCode;
+}
+
+/*
+** Set the error code and message for the Parse object passed as the
+** first argument.
+*/
+void xjd1ParseError(Parse *p, int errCode, const char *zFormat, ...){
+  va_list ap;
+  if( p->errCode==XJD1_OK ){
+    p->errCode = errCode;
+    va_start(ap, zFormat);
+    xjd1StringVAppendF(&p->errMsg, zFormat, ap);
+    va_end(ap);
+  }
+}

+ 396 - 0
unql/src/trace.c

@@ -0,0 +1,396 @@
+/*tab
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code used to render the parse tree.
+*/
+#include "xjd1Int.h"
+
+/*
+** Names of tokens.
+*/
+static const struct {
+  int code;
+  const char *zName;
+} aToken[] = {
+  /* Begin paste of parse_txt.h */
+  { TK_SEMI,             "TK_SEMI"            },
+  { TK_INTEGER,          "TK_INTEGER"         },
+  { TK_FLOAT,            "TK_FLOAT"           },
+  { TK_STRING,           "TK_STRING"          },
+  { TK_TRUE,             "TK_TRUE"            },
+  { TK_FALSE,            "TK_FALSE"           },
+  { TK_NULL,             "TK_NULL"            },
+  { TK_QM,               "TK_QM"              },
+  { TK_OR,               "TK_OR"              },
+  { TK_AND,              "TK_AND"             },
+  { TK_BITOR,            "TK_BITOR"           },
+  { TK_BITXOR,           "TK_BITXOR"          },
+  { TK_BITAND,           "TK_BITAND"          },
+  { TK_ILIKEOP,          "TK_ILIKEOP"         },
+  { TK_LIKEOP,           "TK_LIKEOP"          },
+  { TK_NE,               "TK_NE"              },
+  { TK_EQEQ,             "TK_EQEQ"            },
+  { TK_EQ3,              "TK_EQ3"             },
+  { TK_NE3,              "TK_NE3"             },
+  { TK_WITHIN,           "TK_WITHIN"          },
+  { TK_IN,               "TK_IN"              },
+  { TK_GT,               "TK_GT"              },
+  { TK_LE,               "TK_LE"              },
+  { TK_LT,               "TK_LT"              },
+  { TK_GE,               "TK_GE"              },
+  { TK_LSHIFT,           "TK_LSHIFT"          },
+  { TK_RSHIFT,           "TK_RSHIFT"          },
+  { TK_URSHIFT,          "TK_URSHIFT"         },
+  { TK_PLUS,             "TK_PLUS"            },
+  { TK_MINUS,            "TK_MINUS"           },
+  { TK_STAR,             "TK_STAR"            },
+  { TK_SLASH,            "TK_SLASH"           },
+  { TK_REM,              "TK_REM"             },
+  { TK_BITNOT,           "TK_BITNOT"          },
+  { TK_BANG,             "TK_BANG"            },
+  { TK_COLLATE,          "TK_COLLATE"         },
+  { TK_ID,               "TK_ID"              },
+  { TK_DOT,              "TK_DOT"             },
+  { TK_LB,               "TK_LB"              },
+  { TK_RB,               "TK_RB"              },
+  { TK_LC,               "TK_LC"              },
+  { TK_RC,               "TK_RC"              },
+  { TK_COLON,            "TK_COLON"           },
+  { TK_COMMA,            "TK_COMMA"           },
+  { TK_LP,               "TK_LP"              },
+  { TK_RP,               "TK_RP"              },
+  { TK_UNION,            "TK_UNION"           },
+  { TK_EXCEPT,           "TK_EXCEPT"          },
+  { TK_INTERSECT,        "TK_INTERSECT"       },
+  { TK_ALL,              "TK_ALL"             },
+  { TK_SELECT,           "TK_SELECT"          },
+  { TK_DISTINCT,         "TK_DISTINCT"        },
+  { TK_AS,               "TK_AS"              },
+  { TK_FROM,             "TK_FROM"            },
+  { TK_FLATTENOP,        "TK_FLATTENOP"       },
+  { TK_GROUP,            "TK_GROUP"           },
+  { TK_BY,               "TK_BY"              },
+  { TK_HAVING,           "TK_HAVING"          },
+  { TK_ORDER,            "TK_ORDER"           },
+  { TK_ASCENDING,        "TK_ASCENDING"       },
+  { TK_DESCENDING,       "TK_DESCENDING"      },
+  { TK_LIMIT,            "TK_LIMIT"           },
+  { TK_OFFSET,           "TK_OFFSET"          },
+  { TK_WHERE,            "TK_WHERE"           },
+  { TK_BEGIN,            "TK_BEGIN"           },
+  { TK_ROLLBACK,         "TK_ROLLBACK"        },
+  { TK_COMMIT,           "TK_COMMIT"          },
+  { TK_CREATE,           "TK_CREATE"          },
+  { TK_COLLECTION,       "TK_COLLECTION"      },
+  { TK_IF,               "TK_IF"              },
+  { TK_NOT,              "TK_NOT"             },
+  { TK_EXISTS,           "TK_EXISTS"          },
+  { TK_DROP,             "TK_DROP"            },
+  { TK_DELETE,           "TK_DELETE"          },
+  { TK_UPDATE,           "TK_UPDATE"          },
+  { TK_SET,              "TK_SET"             },
+  { TK_EQ,               "TK_EQ"              },
+  { TK_ELSE,             "TK_ELSE"            },
+  { TK_INSERT,           "TK_INSERT"          },
+  { TK_INTO,             "TK_INTO"            },
+  { TK_VALUE,            "TK_VALUE"           },
+  { TK_ASYNC,            "TK_ASYNC"           },
+  { TK_SYNC,             "TK_SYNC"            },
+  { TK_PRAGMA,           "TK_PRAGMA"          },
+};
+
+/*
+** Return the name of a token.
+*/
+const char *xjd1TokenName(int code){
+  unsigned int i;
+  for(i=0; i<sizeof(aToken)/sizeof(aToken[0]); i++){
+    if( aToken[i].code==code ) return aToken[i].zName+3;
+  }
+  return "<unknown>";
+}
+
+/*
+** Output the content of a Command structure.
+*/
+void xjd1TraceCommand(String *pOut, int indent, const Command *pCmd){
+  if( pCmd==0 ) return;
+  switch( pCmd->eCmdType ){
+    case TK_SELECT: {
+      xjd1StringAppendF(pOut, "%*sQuery:\n", indent, "");
+      xjd1TraceQuery(pOut, indent+3, pCmd->u.q.pQuery);
+      break;
+    }
+    case TK_CREATECOLLECTION: {
+      xjd1StringAppendF(pOut, "%*sCreate-Collection: \"%s\" if-not-exists=%d\n",
+         indent, "", pCmd->u.crtab.zName,
+         pCmd->u.crtab.ifExists);
+      break;
+    }
+    case TK_DROPCOLLECTION: {
+      xjd1StringAppendF(pOut, "%*sDrop-Collection: \"%s\" if-exists=%d\n",
+         indent, "", pCmd->u.crtab.zName,
+         pCmd->u.crtab.ifExists);
+      break;
+    }
+    case TK_INSERT: {
+      xjd1StringAppendF(pOut, "%*sInsert: %s\n",
+         indent, "", pCmd->u.ins.zName);
+      if( pCmd->u.ins.pValue ){
+         xjd1StringAppendF(pOut, "%*s value: ", indent, "");
+         xjd1TraceExpr(pOut, pCmd->u.ins.pValue);
+         xjd1StringAppend(pOut, "\n", 1);
+      }else{
+         xjd1TraceQuery(pOut, indent+3, pCmd->u.ins.pQuery);
+      }
+      break; 
+    }
+    case TK_DELETE: {
+      xjd1StringAppendF(pOut, "%*sDELETE: %s\n",
+         indent, "", pCmd->u.del.zName);
+      if( pCmd->u.del.pWhere ){
+         xjd1StringAppendF(pOut, "%*s WHERE ", indent, "");
+         xjd1TraceExpr(pOut, pCmd->u.del.pWhere);
+         xjd1StringAppend(pOut, "\n", 1);
+      }
+      break; 
+    }
+    case TK_UPDATE: {
+      xjd1StringAppendF(pOut, "%*sUPDATE: %s\n",
+         indent, "", pCmd->u.update.zName);
+      xjd1TraceExprList(pOut, indent+3, pCmd->u.update.pChng);
+      if( pCmd->u.update.pWhere ){
+         xjd1StringAppendF(pOut, "%*s WHERE ", indent, "");
+         xjd1TraceExpr(pOut, pCmd->u.update.pWhere);
+         xjd1StringAppend(pOut, "\n", 1);
+      }
+      break; 
+    }
+    case TK_PRAGMA: {
+      xjd1StringAppendF(pOut, "%*sPRAGMA: %s",
+         indent, "", pCmd->u.prag.zName);
+      if( pCmd->u.prag.pValue ){
+        xjd1StringAppend(pOut, "(", 1);
+        xjd1TraceExpr(pOut, pCmd->u.ins.pValue);
+        xjd1StringAppend(pOut, ")\n", 2);
+      }else{
+        xjd1StringAppend(pOut, "\n", 1);
+      }
+      break; 
+    }
+    default: {
+      xjd1StringAppendF(pOut, "%*seCmdType = %s (%d)\n",
+          indent, "", xjd1TokenName(pCmd->eCmdType), pCmd->eCmdType);
+      break;
+    }
+  }
+}
+
+
+/*
+** Output the content of an query.
+*/
+void xjd1TraceQuery(String *pOut, int indent, const Query *p){
+  if( p==0 ) return;
+  xjd1StringAppendF(pOut, "%*seQType = %s\n",
+          indent, "", xjd1TokenName(p->eQType));
+  switch( p->eQType ){
+    case TK_SELECT: {
+      if( p->u.simple.pRes ){
+        xjd1StringAppendF(pOut, "%*sResult: ", indent, "");
+        xjd1TraceExpr(pOut, p->u.simple.pRes);
+        xjd1StringAppend(pOut, "\n", 1);
+      }
+      if( p->u.simple.pFrom ){
+        xjd1StringAppendF(pOut, "%*sFROM:\n", indent, "");
+        xjd1TraceDataSrc(pOut, indent+3, p->u.simple.pFrom);
+      }
+      if( p->u.simple.pWhere ){
+        xjd1StringAppendF(pOut, "%*sWHERE: ", indent, "");
+        xjd1TraceExpr(pOut, p->u.simple.pWhere);
+        xjd1StringAppendF(pOut, "\n");
+      }
+      if( p->u.simple.pGroupBy ){
+        xjd1StringAppendF(pOut, "%*sGROUP-BY:\n", indent, "");
+        xjd1TraceExprList(pOut, indent+3, p->u.simple.pGroupBy);
+      }
+      if( p->u.simple.pHaving ){
+        xjd1StringAppendF(pOut, "%*sHAVING: ", indent, "");
+        xjd1TraceExpr(pOut, p->u.simple.pWhere);
+        xjd1StringAppendF(pOut, "\n");
+      }
+      break;
+    }
+    default: {
+      if( p->u.compound.pLeft ){
+        xjd1StringAppendF(pOut, "%*sLeft:\n", indent, "");
+        xjd1TraceQuery(pOut, indent+3, p->u.compound.pLeft);
+      }
+      if( p->u.compound.pRight ){
+        xjd1StringAppendF(pOut, "%*sLeft:\n", indent, "");
+        xjd1TraceQuery(pOut, indent+3, p->u.compound.pRight);
+      }
+      break;
+    }
+
+    if( p->pOrderBy ){
+      xjd1StringAppendF(pOut, "%*sORDER-BY:\n", indent, "");
+      xjd1TraceExprList(pOut, indent+3, p->pOrderBy);
+    }
+    if( p->pLimit ){
+      xjd1StringAppendF(pOut, "%*sLIMIT: ", indent, "");
+      xjd1TraceExpr(pOut, p->pLimit);
+      xjd1StringAppendF(pOut, "\n");
+    }
+    if( p->pOffset ){
+      xjd1StringAppendF(pOut, "%*sOFFSET: ", indent, "");
+      xjd1TraceExpr(pOut, p->pOffset);
+      xjd1StringAppendF(pOut, "\n");
+    }
+  }
+}
+
+/*
+** Output the content of a data source.
+*/
+void xjd1TraceDataSrc(String *pOut, int indent, const DataSrc *p){
+  if( p==0 ) return;
+  /*xjd1StringAppendF(pOut, "%*s%s\n", indent, "", xjd1TokenName(p->eDSType));*/
+  switch( p->eDSType ){
+    case TK_ID: {
+      xjd1StringAppendF(pOut, "%*sID.%s", indent, "", p->u.tab.zName);
+      if( p->zAs ){
+        xjd1StringAppendF(pOut, " AS %s", p->zAs);
+      }
+      xjd1StringAppend(pOut, "\n", 1);
+      break;
+    }
+    case TK_COMMA: {
+      xjd1StringAppendF(pOut, "%*sJoin:\n", indent, "");
+      xjd1TraceDataSrc(pOut, indent+3, p->u.join.pLeft);
+      xjd1TraceDataSrc(pOut, indent+3, p->u.join.pRight);
+      break;
+    }
+    case TK_SELECT: {
+      xjd1StringAppendF(pOut, "%*sSubquery:\n", indent, "");
+      xjd1TraceQuery(pOut, indent+3, p->u.subq.q);
+      break;
+    }
+    case TK_FLATTENOP: {
+      xjd1TraceDataSrc(pOut, indent, p->u.flatten.pNext);
+      xjd1StringAppendF(pOut, "%*s%s:\n", indent, "",
+            p->u.flatten.cOpName=='E' ? "EACH" : "FLATTEN");
+      xjd1TraceExpr(pOut, p->u.flatten.pExpr);
+      xjd1StringAppendF(pOut, " AS ");
+      xjd1TraceExpr(pOut, p->u.flatten.pAs);
+      break;
+    }
+  }
+}
+
+/*
+** Output the content of an expression.
+*/
+void xjd1TraceExpr(String *pOut, const Expr *p){
+  if( p==0 ) return;
+  switch( p->eType ){
+    case TK_JVALUE: {
+      xjd1JsonRender(pOut, p->u.json.p);
+      break;
+    }
+    case TK_STRUCT: {
+      int i;
+      ExprList *pList = p->u.st;
+      xjd1StringAppend(pOut, "{", 1);
+      for(i=0; i<pList->nEItem; i++){
+        ExprItem *pItem = &pList->apEItem[i];
+        if( i>0 ) xjd1StringAppend(pOut, ",", 1);
+        xjd1StringAppendF(pOut, "%s:", pItem->zAs);
+        xjd1TraceExpr(pOut, pItem->pExpr);
+      }
+      xjd1StringAppend(pOut, "}", 1);
+      break;
+    }
+    case TK_ARRAY: {
+      int i;
+      ExprList *pList = p->u.st;
+      xjd1StringAppend(pOut, "[", 1);
+      for(i=0; i<pList->nEItem; i++){
+        ExprItem *pItem = &pList->apEItem[i];
+        if( i>0 ) xjd1StringAppend(pOut, ",", 1);
+        xjd1TraceExpr(pOut, pItem->pExpr);
+      }
+      xjd1StringAppend(pOut, "]", 1);
+      break;
+    }
+    case TK_DOT:
+    case TK_AND:
+    case TK_OR:
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_EQ:
+    case TK_NE:
+    case TK_ILIKEOP:
+    case TK_LIKEOP:
+    case TK_BITAND:
+    case TK_BITOR:
+    case TK_BITNOT:
+    case TK_LSHIFT:
+    case TK_RSHIFT:
+    case TK_PLUS:
+    case TK_MINUS:
+    case TK_STAR:
+    case TK_SLASH:
+    case TK_REM:
+    case TK_BANG: {
+      if( p->u.bi.pRight ){
+        xjd1StringAppend(pOut, "(", 1);
+        xjd1TraceExpr(pOut, p->u.bi.pLeft);
+        xjd1StringAppendF(pOut, ") %s (", xjd1TokenName(p->eType));
+        xjd1TraceExpr(pOut, p->u.bi.pRight);
+        xjd1StringAppend(pOut, ")", 1);
+      }else{
+        xjd1StringAppendF(pOut, "%s (", xjd1TokenName(p->eType));
+        xjd1TraceExpr(pOut, p->u.bi.pLeft);
+        xjd1StringAppend(pOut, ")", 1);
+      }
+      break;
+    }
+    default: {
+      xjd1StringAppend(pOut, xjd1TokenName(p->eType), -1);
+      break;
+    }
+  }
+}
+
+/*
+** Output the content of an expression list.
+*/
+void xjd1TraceExprList(String *pOut, int indent, const ExprList *p){
+  int i;
+  if( p==0 || p->nEItem==0 ) return;
+  for(i=0; i<p->nEItem; i++){
+    xjd1StringAppendF(pOut, "%*s%d: ", indent, "", i);
+    xjd1TraceExpr(pOut, p->apEItem[i].pExpr);
+    if( p->apEItem[i].zAs ){
+      xjd1StringAppendF(pOut, " AS %s\n", p->apEItem[i].zAs);
+    }else{
+      xjd1StringAppend(pOut, "\n", 1);
+    }
+  }
+}

+ 240 - 0
unql/src/update.c

@@ -0,0 +1,240 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Code to evaluate an UPDATE statement
+*/
+#include "xjd1Int.h"
+
+
+/*
+** pBase is a JSON structure object.  If it is not, overwrite the current
+** value with an empty structure object. [[TBD: If it is not, return 0;]]
+** Otherwise, lookup or insert the zField element.
+*/
+static JsonNode *findStructElement(JsonNode *pBase, const char *zField){
+  JsonStructElem *pElem;
+  if( pBase==0 ) return 0;
+  if( pBase->eJType!=XJD1_STRUCT ){
+//    return 0;
+    xjd1JsonToNull(pBase);
+    pBase->eJType = XJD1_STRUCT;
+    pBase->u.st.pFirst = 0;
+    pBase->u.st.pLast = 0;
+  }
+  for(pElem=pBase->u.st.pFirst; pElem; pElem=pElem->pNext){
+    if( strcmp(pElem->zLabel, zField)==0 ){
+      return pElem->pValue;
+    }
+  }
+  pElem = xjd1_malloc( sizeof(*pElem) );
+  if( pElem==0 ) return 0;
+  pElem->pNext = 0;
+  if( pBase->u.st.pLast==0 ){
+    pBase->u.st.pFirst = pElem;
+  }else{
+    pBase->u.st.pLast->pNext = pElem;
+  }
+  pBase->u.st.pLast = pElem;
+  pElem->zLabel = xjd1PoolDup(0, zField, -1);
+  pElem->pValue = xjd1JsonNew(0);
+  return pElem->pValue;
+}
+
+/*
+** The expression p is an L-value.  Find the corresponding JsonNode.
+** Create it if necessary.
+*/
+static JsonNode *findOrCreateJsonNode(JsonNode *pRoot, Expr *p){
+  if( p==0 ) return 0;
+  switch( p->eType ){
+    /* The x.y operator. Here y must be an identifier, and we use its
+    ** name string. The reference node is property y of object x.
+    */
+    case TK_DOT: {
+      JsonNode *pBase = findOrCreateJsonNode(pRoot, p->u.lvalue.pLeft);
+      return findStructElement(pBase, p->u.lvalue.zId);
+    }
+
+    /* The x[y] operator. The result depends on the type of value x.
+    **
+    ** If x is of type XJD1_STRUCT, then expression y is converted to
+    ** a string. The reference node is property y of object x.
+    **
+    ** If x is of type XJD1_ARRAY, then expression y is converted to
+    ** a number. If that number is an integer, then the reference node
+    ** is element y of array x. Grow the array as needed.
+    **
+    ** If x is of any other type the assignment is silently ignored.
+    ** Note that individual string characters can be read, but not
+    ** written using the x[y] syntax.
+    ** TBD: should x===null throw an error?
+    */
+    case TK_LB: {
+      JsonNode *pBase = findOrCreateJsonNode(pRoot, p->u.bi.pLeft);
+      switch( pBase->eJType ){
+        case XJD1_STRUCT: {
+          JsonNode *pRes;
+          String idx;
+          xjd1StringInit(&idx, 0, 0);
+          xjd1JsonToString(xjd1ExprEval(p->u.bi.pRight), &idx);
+          pRes = findStructElement(pBase, idx.zBuf);
+          xjd1StringClear(&idx);
+          return pRes;
+        }
+
+        case XJD1_ARRAY: {
+          double rRight;
+          int iIdx;
+          if( xjd1JsonToReal(xjd1ExprEval(p->u.bi.pRight), &rRight) ) break;
+          iIdx = (int)rRight;
+          if( (double)iIdx==rRight && iIdx>=0 ){
+            if( iIdx<pBase->u.ar.nElem ){
+              return pBase->u.ar.apElem[iIdx];
+            }else{
+              JsonNode **pNewArray;
+              int i;
+              /* Grow the array as needed. TBD: find a way to handle very
+              ** large, sparse arrays. As a temporary drafting precaution,
+              ** assert that the value of iIdx is less than 1024.
+              */
+              assert( iIdx<1024 );
+              pNewArray = xjd1_realloc(pBase->u.ar.apElem, sizeof(JsonNode*)*(iIdx+1));
+              if( pNewArray==0 ) break;
+              pBase->u.ar.apElem = pNewArray;
+              for(i=pBase->u.ar.nElem; i<=iIdx; i++) {
+                pNewArray[i] = xjd1JsonNew(0);
+                pNewArray[i]->eJType = XJD1_NULL;
+              }
+              pBase->u.ar.nElem = iIdx+1;
+              return pBase->u.ar.apElem[iIdx];
+            }
+          }
+          break;
+        }
+
+        case XJD1_NULL: {
+          /* TBD: throw error? */
+          break;
+        }
+      }
+    }
+    break;
+
+    case TK_ID: {
+      return pRoot;
+    }
+  }
+  return 0;
+}
+
+/*
+** Perform an edit on a JSON value.  Return the document after the change.
+*/
+static void reviseOneField(
+  JsonNode *pDoc,        /* The document to be edited */
+  Expr *pLvalue,         /* Definition of field in document to be changed */
+  Expr *pValue           /* New value for the field */
+){
+  JsonNode *pNode;
+  JsonNode *pX;
+
+  pNode = findOrCreateJsonNode(pDoc, pLvalue);
+  if( pNode ){
+    pX = xjd1JsonEdit(xjd1ExprEval(pValue));
+    xjd1JsonToNull(pNode);
+    *pNode = *pX;
+    xjd1_free(pX);
+  }
+}
+
+
+/*
+** Evaluate an UPDATE.
+*/
+int xjd1UpdateStep(xjd1_stmt *pStmt){
+  Command *pCmd = pStmt->pCmd;
+  int rc = XJD1_OK;
+  int nUpdate = 0;
+  sqlite3 *db = pStmt->pConn->db;
+  sqlite3_stmt *pQuery, *pReplace;
+  char *zSql;
+  int inAutocommit = sqlite3_get_autocommit(db);
+
+  assert( pCmd!=0 );
+  assert( pCmd->eCmdType==TK_UPDATE );
+  if(inAutocommit) sqlite3_exec(db, "BEGIN", 0, 0, 0);
+  zSql = sqlite3_mprintf("SELECT rowid, x FROM \"%w\"", pCmd->u.update.zName);
+  sqlite3_prepare_v2(db, zSql, -1, &pQuery, 0);
+  sqlite3_free(zSql);
+  zSql = sqlite3_mprintf("UPDATE \"%w\" SET x=?1 WHERE rowid=?2",
+                         pCmd->u.update.zName);
+  sqlite3_prepare_v2(db, zSql, -1, &pReplace, 0);
+  sqlite3_free(zSql);
+  if( pQuery && pReplace ){
+    while( SQLITE_ROW==sqlite3_step(pQuery) ){
+      const char *zJson = (const char*)sqlite3_column_text(pQuery, 1);
+      pStmt->pDoc = xjd1JsonParse(zJson, -1);
+      if( pCmd->u.update.pWhere==0 || xjd1ExprTrue(pCmd->u.update.pWhere) ){
+        JsonNode *pNewDoc;  /* Revised document content */
+        ExprList *pChng;    /* List of changes */
+        String jsonNewDoc;  /* Text rendering of revised document */
+        int i, n;
+
+        pNewDoc = xjd1JsonEdit(xjd1JsonRef(pStmt->pDoc));
+        pChng = pCmd->u.update.pChng;
+        n = pChng->nEItem;
+        for(i=0; i<n-1; i += 2){
+          Expr *pLvalue = pChng->apEItem[i].pExpr;
+          Expr *pExpr = pChng->apEItem[i+1].pExpr;
+          reviseOneField(pNewDoc, pLvalue, pExpr);
+        }
+        xjd1StringInit(&jsonNewDoc, 0, 0);
+        xjd1JsonRender(&jsonNewDoc, pNewDoc);
+        sqlite3_bind_int64(pReplace, 2, sqlite3_column_int64(pQuery, 0));
+        sqlite3_bind_text(pReplace, 1, xjd1StringText(&jsonNewDoc), -1,
+                          SQLITE_STATIC);
+        sqlite3_step(pReplace);
+        sqlite3_reset(pReplace);
+        xjd1StringClear(&jsonNewDoc);
+        xjd1JsonFree(pNewDoc);
+        nUpdate++;
+      }
+      xjd1JsonFree(pStmt->pDoc);
+      pStmt->pDoc = 0;
+    }
+  }
+  sqlite3_finalize(pQuery);
+  sqlite3_finalize(pReplace);
+
+  if( pCmd->u.update.pUpsert ){
+    if( nUpdate==0 ){
+      JsonNode *pToIns;
+      String jsonToIns;
+      pToIns = xjd1ExprEval(pCmd->u.update.pUpsert);
+      xjd1StringInit(&jsonToIns, 0, 0);
+      xjd1JsonRender(&jsonToIns, pToIns);
+      xjd1JsonFree(pToIns);
+      zSql = sqlite3_mprintf(
+           "INSERT INTO \"%w\" VALUES('%q')",
+           pCmd->u.update.zName, xjd1StringText(&jsonToIns));
+      xjd1StringClear(&jsonToIns);
+      sqlite3_exec(db, zSql, 0, 0, 0);
+      sqlite3_free(zSql);
+    }
+  }
+  if(inAutocommit) sqlite3_exec(db, "COMMIT", 0, 0, 0);
+  return rc;
+}

+ 97 - 0
unql/src/xjd1.h

@@ -0,0 +1,97 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Interface definitions for XJD1
+*/
+#ifndef _XJD1_H
+#define _XJD1_H
+
+/* Return codes */
+#define XJD1_OK       0      /* No error */
+#define XJD1_ERROR    1      /* Generic error */
+#define XJD1_MISUSE   2      /* Misuse of the interface */
+#define XJD1_NOMEM    3      /* Out of heap memory */
+#define XJD1_UNKNOWN  4      /* Unknown configuration option */
+#define XJD1_SYNTAX   5      /* Syntax error */
+#define XJD1_ERROR_OPEN_DB   6      /* Error opening a database */
+
+#define XJD1_ROW      200    /* Another row available */
+#define XJD1_DONE     201    /* query complete */
+
+
+/* Execution context */
+typedef struct xjd1_context xjd1_context;
+
+/* A database connection */
+typedef struct xjd1 xjd1;
+
+/* A prepared statement */
+typedef struct xjd1_stmt xjd1_stmt;
+
+/* Create, setup, and destroy an execution context */
+int xjd1_context_new(xjd1_context**);
+int xjd1_context_config(xjd1_context*, int, ...);
+int xjd1_context_delete(xjd1_context*);
+
+/* Operators for xjd1_context_config() */
+#define XJD1_CONTEXT_LOG   1
+
+/* Open and close a database connection */
+int xjd1_open(xjd1_context*, const char *zURI, xjd1**);
+int xjd1_config(xjd1*, int, ...);
+int xjd1_close(xjd1*);
+
+/* sqlite3 database */
+typedef struct sqlite3 sqlite3;
+int xjd1_open_with_db(xjd1_context*, sqlite3 *db, xjd1**);
+
+/* Operators for xjd1_config() */
+#define XJD1_CONFIG_PARSERTRACE    1
+
+/* Report on recent errors */
+int xjd1_errcode(xjd1*);
+const char *xjd1_errmsg(xjd1*);
+const char *xjd1_errcode_name(xjd1*);
+
+/* Create a new prepared statement */
+int xjd1_stmt_new(xjd1*, const char*, xjd1_stmt**, int*);
+int xjd1_stmt_delete(xjd1_stmt*);
+int xjd1_stmt_config(xjd1_stmt*, int, ...);
+
+/* Process a prepared statement */
+int xjd1_stmt_step(xjd1_stmt*);
+int xjd1_stmt_rewind(xjd1_stmt*);
+int xjd1_stmt_value(xjd1_stmt*, const char**);
+
+/* Return true if zStmt is a complete query statement */
+int xjd1_complete(const char *zStmt);
+
+/* Show the internal structure of a statement */
+char *xjd1_stmt_debug_listing(xjd1_stmt*);
+
+
+/* Configure or access the malloc/realloc/free used by the module. This
+** will change. */
+void xjd1_configure_malloc(
+  void *(*xMalloc)(int),
+  void *(*xRealloc)(void *, int),
+  void (*xFree)(void *)
+);
+void *xjd1_malloc(int N);
+void xjd1_free(void *p);
+void *xjd1_realloc(void *p, int N);
+
+#endif /* _XJD1_H */

+ 528 - 0
unql/src/xjd1Int.h

@@ -0,0 +1,528 @@
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+**   [email protected]
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** Internal definitions for XJD1
+*/
+#ifndef _XJD1INT_H
+#define _XJD1INT_H
+
+#include "xjd1.h"
+#include "parse.h"
+#include "sqlite3.h"
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+/* Marker for routines not intended for external use */
+#define PRIVATE
+
+/* Additional tokens above and beyond those generated by the parser and
+** found in parse.h
+*/
+#define TK_NOT_LIKEOP        (TK_LIKEOP+128)
+#define TK_FUNCTION          100
+#define TK_SPACE             101
+#define TK_ILLEGAL           102
+#define TK_CREATECOLLECTION  103
+#define TK_DROPCOLLECTION    104
+#define TK_ARRAY             105
+#define TK_STRUCT            106
+#define TK_JVALUE            107
+
+/*
+** A convenience macro for returning the size of an fixed-size array.
+** For example:
+**
+**     DataType var[N];
+**     assert( ArraySize(var)==N );
+*/
+#define ArraySize(X)    ((int)(sizeof(X)/sizeof(X[0])))
+
+typedef unsigned char u8;
+typedef unsigned short int u16;
+typedef struct AggExpr AggExpr;
+typedef struct Aggregate Aggregate;
+typedef struct Command Command;
+typedef struct DataSrc DataSrc;
+typedef struct Expr Expr;
+typedef struct ExprItem ExprItem;
+typedef struct ExprList ExprList;
+typedef struct FlattenIter FlattenIter;
+typedef struct Function Function;
+typedef struct JsonNode JsonNode;
+typedef struct JsonStructElem JsonStructElem;
+typedef struct Parse Parse;
+typedef struct PoolChunk PoolChunk;
+typedef struct Pool Pool;
+typedef struct Query Query;
+typedef struct String String;
+typedef struct Token Token;
+typedef struct ResultList ResultList;
+typedef struct ResultItem ResultItem;
+
+/* A single allocation from the Pool allocator */
+struct PoolChunk {
+  PoolChunk *pNext;                 /* Next chunk on list of them all */
+};
+
+/* A memory allocation pool */
+struct Pool {
+  PoolChunk *pChunk;                /* List of all memory allocations */
+  char *pSpace;                     /* Space available for allocation */
+  int nSpace;                       /* Bytes available in pSpace */
+};
+
+/* A variable length string */
+struct String {
+  Pool *pPool;                      /* Memory allocation pool or NULL */
+  char *zBuf;                       /* String content */
+  int nUsed;                        /* Slots used.  Not counting final 0 */
+  int nAlloc;                       /* Space allocated */
+};
+
+/* Execution context */
+struct xjd1_context {
+  int nRef;                         /* Reference count */
+  u8 isDying;                       /* True if has been deleted */
+  int (*xLog)(const char*,void*);   /* Error logging function */
+  void *pLogArg;                    /* 2nd argument to xLog() */
+};
+
+/* An open database connection */
+struct xjd1 {
+  xjd1_context *pContext;           /* Execution context */
+  int nRef;                         /* Reference count */
+  u8 isDying;                       /* True if has been closed */
+  u8 parserTrace;                   /* True to enable parser tracing */
+  u8 appendErr;                     /* append errMsg rather than overwrite */
+  u8 isSQLite3Borrowed;             /* We'll not close this database as it is borrowed */
+  xjd1_stmt *pStmt;                 /* list of all prepared statements */
+  sqlite3 *db;                      /* Storage engine */
+  int errCode;                      /* Latest non-zero error code */
+  String errMsg;                    /* Latest error message */
+};
+
+/* A prepared statement */
+struct xjd1_stmt {
+  xjd1 *pConn;                      /* Database connection */
+  xjd1_stmt *pNext, *pPrev;         /* List of all statements */
+  Pool sPool;                       /* Memory pool used for parsing */
+  int nRef;                         /* Reference count */
+  u8 isDying;                       /* True if has been closed */
+  char *zCode;                      /* Text of the query */
+  Command *pCmd;                    /* Parsed command */
+  JsonNode *pDoc;                   /* Current document */
+  int okValue;                      /* True if retValue is valid */
+  String retValue;                  /* String rendering of return value */
+
+  int errCode;                      /* Error code */
+  String errMsg;                    /* Error message */
+};
+
+/* A token into to the parser */
+struct Token {
+  const char *z;                    /* Text of the token */
+  int n;                            /* Number of characters */
+};
+
+/* A single element of an expression list */
+struct ExprItem {
+  char *zAs;                /* AS value, or DESCENDING, or ASCENDING */
+  Expr *pExpr;              /* The expression */
+};
+
+/* A list of expressions */
+struct ExprList {
+  int nEItem;               /* Number of items on the expression list */
+  int nEAlloc;              /* Slots allocated in apEItem[] */
+  ExprItem *apEItem;        /* The expression in the list */
+};
+
+/*
+** A node of an expression.
+**
+** If (eClass==EXPR_TK), then this node is a reference to a data-source, or
+** if the expression is part of a SELECT query, to the result of the query
+** itself. If this expression is part of any type of statement other than a
+** SELECT, then iDatasrc and pQuery are both set to 0. In that case there is
+** only one data-source to which the expression could refer.
+**
+** If the expression is part of a SELECT and the iDatasrc field is set to
+** 0, then the expression refers to the return value of the SELECT statement
+** pQuery. pQuery may point to the simple SELECT that the expression is part
+** of, or to some other SELECT statement if the expression is part of a
+** correlated sub-select. If iDatasrc is set to N, where N is 1 or greater,
+** it refers to the Nth data-source joined together in the FROM clause of
+** pQuery, counting from left to right.
+*/
+struct Expr {
+  u16 eType;                /* Expression node type */
+  u16 eClass;               /* Expression class */
+  Query *pQuery;            /* Query this expression belongs to.  May be NULL */
+  xjd1_stmt *pStmt;         /* Statement this expression belongs to */
+  union {
+    struct {                /* Binary or unary operator. eClass==XJD1_EXPR_BI */
+      Expr *pLeft;             /* Left operand.  Only operand for unary ops */
+      Expr *pRight;            /* Right operand.  NULL for unary ops */
+    } bi;
+    struct {                /* Substructure nam.  eClass==EXPR_LVALUE */
+      Expr *pLeft;             /* Lvalue or id to the left */
+      char *zId;               /* ID to the right */
+    } lvalue;
+    struct {                /* Identifiers */
+      char *zId;               /* token value.  eClass=EXPR_TK */
+      int iDatasrc;
+      Query *pQuery;
+    } id;
+    struct {                /* Function calls.  eClass=EXPR_FUNC */
+      char *zFName;            /* Name of the function */
+      ExprList *args;          /* List of arguments */
+      Function *pFunction;     /* Function object */
+      JsonNode **apArg;        /* Array to martial function arguments in */
+      int iAgg;
+    } func;
+    struct {                /* Subqueries.  eClass=EXPR_Q */
+      Query *p;                /* The subquery */
+    } subq;
+    struct {                /* Literal value.  eClass=EXPR_JSON */
+      JsonNode *p;             /* The value */
+    } json;
+    ExprList *ar;           /* Array literal.  eClass=EXPR_ARRAY */
+    ExprList *st;           /* Struct literal.  eClass=EXPR_STRUCT */
+    struct {                /* Tertiary operator.  eClass==EXPR_TRI */
+      Expr *pTest;          /* A in A?B:C */
+      Expr *pIfTrue;        /* B in A?B:C */
+      Expr *pIfFalse;       /* C in A?B:C */
+    } tri;
+  } u;
+};
+#define XJD1_EXPR_BI      1
+#define XJD1_EXPR_TK      2
+#define XJD1_EXPR_FUNC    3
+#define XJD1_EXPR_Q       4
+#define XJD1_EXPR_JSON    5
+#define XJD1_EXPR_ARRAY   6
+#define XJD1_EXPR_STRUCT  7
+#define XJD1_EXPR_LVALUE  8
+#define XJD1_EXPR_TRI     9
+
+/* A single element of a JSON structure */
+struct JsonStructElem {
+  char *zLabel;             /* Label on this element */
+  JsonStructElem *pNext;    /* Next element of the structure */
+  JsonNode *pValue;         /* Value of this element */
+};
+
+/* A single element of a JSON value */
+struct JsonNode {
+  int eJType;               /* Element type */
+  int nRef;                 /* Number of references */
+  union {
+    int b;                  /* Boolean value */
+    double r;               /* Real value */
+    char *z;                /* String value */
+    struct {                /* Array value */
+      int nElem;               /* Number of elements */
+      JsonNode **apElem;       /* Value of each element */
+    } ar;
+    struct {                /* Struct value */
+      JsonStructElem *pFirst;  /* List of structure elements */
+      JsonStructElem *pLast;   /* Last element of the list */
+    } st;
+  } u;
+};
+
+/* Values for eJType */
+#define XJD1_FALSE     0
+#define XJD1_TRUE      1
+#define XJD1_REAL      2
+#define XJD1_NULL      3
+#define XJD1_STRING    4
+#define XJD1_ARRAY     5
+#define XJD1_STRUCT    6
+
+/* Parsing context */
+struct Parse {
+  xjd1 *pConn;                    /* Connect for recording errors */
+  Pool *pPool;                    /* Memory allocation pool */
+  Command *pCmd;                  /* Results */
+  Token sTok;                     /* Last token seen */
+  int errCode;                    /* Error code */
+  String errMsg;                  /* Error message string */
+};
+
+/* A list of sorted results. */
+struct ResultList {
+  Pool *pPool;
+  int nKey;
+  ResultItem *pSaved;
+  ResultItem *pItem;
+};
+
+struct Aggregate {
+  int nExpr;                      /* Number of aggregate functions */
+  struct AggExpr {
+    Expr *pExpr;                  /* Array of aggregate functions */
+    JsonNode *pValue;             /* Value returned by xFinal() */
+    void *pAggCtx;                /* Context pointer used by implementation */
+  } *aAggExpr;
+};
+
+/* A query statement */
+struct Query {
+  int eQType;                   /* Query type */
+  xjd1_stmt *pStmt;             /* Statement this query is part of */
+  union {
+    struct {                    /* For compound queries */
+      Query *pLeft;               /* Left subquery */
+      Query *pRight;              /* Right subquery */
+      int doneLeft;               /* True if left has run to completion */
+      ResultList left;            /* Sorted results of pLeft */
+      ResultList right;           /* Sorted results of pRight */
+      JsonNode *pOut;
+    } compound;
+    struct {                    /* For simple queries */
+      int isDistinct;             /* True if the DISTINCT keyword is present */
+      Expr *pRes;                 /* Result JSON string */
+      DataSrc *pFrom;             /* The FROM clause */
+      Expr *pWhere;               /* The WHERE clause */
+      ExprList *pGroupBy;         /* The GROUP BY clause */
+      Expr *pHaving;              /* The HAVING clause */
+      Aggregate *pAgg;            /* Aggregation info. 0 for non-aggregates */
+      ResultList grouped;         /* Grouped results, for GROUP BY queries */
+      ResultList distincted;      /* Distinct results */
+    } simple;
+  } u;
+  const char *zAs;                /* Alias assigned to result object (if any) */
+  ExprList *pOrderBy;             /* The ORDER BY clause */
+  Expr *pLimit;                   /* The LIMIT clause */
+  Expr *pOffset;                  /* The OFFSET clause */
+  int eDocFrom;                   /* XJD1_FROM_* - configures xjd1QueryDoc() */
+  ResultList ordered;             /* Query results in sorted order */
+  int bLimitValid;                /* Set to true after nLimit is set */
+  int nLimit;                     /* Stop after returning this many more rows */
+};
+
+/* Candidate values for Query.eDocFrom */
+#define XJD1_FROM_DATASRC    0
+#define XJD1_FROM_GROUPED    1
+#define XJD1_FROM_DISTINCTED 2
+#define XJD1_FROM_ORDERED    3
+
+/* A Data Source is a representation of a term out of the FROM clause. */
+struct DataSrc {
+  int eDSType;              /* Source type */
+  char *zAs;                /* The identifier after the AS keyword */
+  Query *pQuery;            /* Query this data source services */
+  JsonNode *pValue;         /* Current value for this data source */
+  int isOwner;              /* True if this DataSrc owns the pOut line */
+  union {
+    struct {                /* For a join.  eDSType==TK_COMMA */
+      int bStart;              /* True if has already started */
+      DataSrc *pLeft;          /* Data source on the left */
+      DataSrc *pRight;         /* Data source on the right */
+    } join;
+    struct {                /* For a named collection.  eDSType==TK_ID */
+      char *zName;             /* The collection name */
+      sqlite3_stmt *pStmt;     /* Cursor for reading content */
+      int eofSeen;             /* True if at EOF */
+    } tab;
+    struct {                /* For a named collection.  eDSType==TK_ID */
+      Expr *pPath;             /* Path to correlated variable */
+      JsonNode *pArray;        /* Value to iterate through */
+      int iNext;               /* Index of next value in pValue to return */
+    } path;
+    struct {                /* EACH() or FLATTEN().  eDSType==TK_FLATTENOP */
+      DataSrc *pNext;          /* Data source to the left */
+      char cOpName;            /* 'E' or 'F' for "EACH" or "FLATTEN" */
+      Expr *pExpr;             /* Expression to flatten on */
+      Expr *pAs;               /* AS path, if any */
+      FlattenIter *pIter;      /* Iterator */
+    } flatten;
+    struct {                /* A subquery.  eDSType==TK_SELECT */
+      Query *q;                /* The subquery */
+    } subq;
+    struct {                /* An empty FROM clause.  eDSType==TK_NULL */
+      int isDone;              /* True if single row already returned */
+    } null;
+  } u;
+};
+
+/* Any command, including but not limited to a query */
+struct Command {
+  int eCmdType;             /* Type of command */
+  union {
+    struct {                /* Transaction control operations */
+      char *zTransId;          /* Transaction name */
+    } trans;
+    struct {                /* Create or drop table */
+      int ifExists;            /* IF [NOT] EXISTS clause */
+      char *zName;             /* Name of table */
+    } crtab;
+    struct {                /* Query statement */
+      Query *pQuery;           /* The query */
+    } q;
+    struct {                /* Insert */
+      char *zName;             /* Table to insert into */
+      Expr *pValue;            /* Value to be inserted */
+      Query *pQuery;           /* Query to insert from */
+    } ins;
+    struct {                /* Delete */
+      char *zName;             /* Table to delete */
+      Expr *pWhere;            /* WHERE clause */
+    } del;
+    struct {                /* Update */
+      char *zName;             /* Table to modify */
+      Expr *pWhere;            /* WHERE clause */
+      ExprList *pChng;         /* Alternating lvalve and new value */
+      Expr *pUpsert;           /* ELSE INSERT value */
+    } update;
+    struct {                /* Pragma */
+      char *zName;             /* Pragma name */
+      Expr *pValue;            /* Argument or empty string */
+    } prag;
+  } u;
+};
+
+/******************************** context.c **********************************/
+void xjd1ContextUnref(xjd1_context*);
+
+/******************************** conn.c *************************************/
+void xjd1Unref(xjd1*);
+void xjd1Error(xjd1*,int,const char*,...);
+
+/******************************** datasrc.c **********************************/
+int xjd1DataSrcInit(DataSrc*,Query*,void*);
+int xjd1DataSrcRewind(DataSrc*);
+int xjd1DataSrcStep(DataSrc*);
+int xjd1DataSrcClose(DataSrc*);
+int xjd1DataSrcCount(DataSrc*);
+JsonNode *xjd1DataSrcDoc(DataSrc*, const char*);
+int xjd1DataSrcCount(DataSrc *);
+JsonNode *xjd1DataSrcCacheRead(DataSrc *, JsonNode **, const char *zDocname);
+void xjd1DataSrcCacheSave(DataSrc *, JsonNode **);
+int xjd1DataSrcResolve(DataSrc *, const char *zDocname);
+JsonNode *xjd1DataSrcRead(DataSrc *, int);
+
+/******************************** delete.c ***********************************/
+int xjd1DeleteStep(xjd1_stmt*);
+
+/******************************** expr.c *************************************/
+int xjd1ExprInit(Expr*, xjd1_stmt*, Query*, int, void *);
+int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*, int, void *);
+JsonNode *xjd1ExprEval(Expr*);
+int xjd1ExprTrue(Expr*);
+int xjd1ExprClose(Expr*);
+int xjd1ExprListClose(ExprList*);
+
+/* Candidates for the 4th parameter to xjd1ExprInit() */
+#define XJD1_EXPR_RESULT  1
+#define XJD1_EXPR_WHERE   2
+#define XJD1_EXPR_GROUPBY 3
+#define XJD1_EXPR_HAVING  4
+#define XJD1_EXPR_ORDERBY 5
+#define XJD1_EXPR_LIMIT   6
+#define XJD1_EXPR_OFFSET  7
+
+/******************************** json.c *************************************/
+JsonNode *xjd1JsonParse(const char *zIn, int mxIn);
+JsonNode *xjd1JsonRef(JsonNode*);
+void xjd1JsonRender(String*, const JsonNode*);
+int xjd1JsonToReal(const JsonNode*, double*);
+int xjd1JsonToString(const JsonNode*, String*);
+int xjd1JsonCompare(const JsonNode*, const JsonNode*, int insensitive);
+JsonNode *xjd1JsonNew(Pool*);
+JsonNode *xjd1JsonEdit(JsonNode*);
+JsonNode *xjd1JsonDeepCopy(JsonNode*);
+void xjd1JsonFree(JsonNode*);
+void xjd1JsonToNull(JsonNode*);
+void xjd1DequoteString(char*,int);
+int xjd1JsonInsert(JsonNode *, const char *, JsonNode *);
+int xjd1JsonTidy(String *, const char *);
+
+/******************************** memory.c ***********************************/
+Pool *xjd1PoolNew(void);
+void xjd1PoolClear(Pool*);
+void xjd1PoolDelete(Pool*);
+void *xjd1PoolMalloc(Pool*, int);
+void *xjd1PoolMallocZero(Pool*, int);
+char *xjd1PoolDup(Pool*, const char *, int);
+void *xjd1MallocZero(int);
+
+/******************************** pragma.c ***********************************/
+int xjd1PragmaStep(xjd1_stmt*);
+
+/******************************** query.c ************************************/
+int xjd1QueryInit(Query*,xjd1_stmt*,void*);
+int xjd1QueryRewind(Query*);
+int xjd1QueryStep(Query*);
+int xjd1QueryClose(Query*);
+JsonNode *xjd1QueryDoc(Query*, int);
+
+/******************************** stmt.c *************************************/
+JsonNode *xjd1StmtDoc(xjd1_stmt*);
+void xjd1StmtError(xjd1_stmt *,int,const char*,...);
+
+/******************************** string.c ***********************************/
+int xjd1Strlen30(const char *);
+void xjd1StringInit(String*, Pool*, int);
+String *xjd1StringNew(Pool*, int);
+char *xjd1StringGet(String*);
+int xjd1StringAppend(String*, const char*, int);
+#define xjd1StringText(S)      ((S)->zBuf)
+#define xjd1StringLen(S)       ((S)->nUsed)
+void xjd1StringTruncate(String*);
+void xjd1StringClear(String*);
+void xjd1StringDelete(String*);
+void xjd1StringRemovePrefix(String*,int);
+int xjd1StringVAppendF(String*, const char*, va_list);
+int xjd1StringAppendF(String*, const char*, ...);
+
+
+/******************************** tokenize.c *********************************/
+extern const unsigned char xjd1CtypeMap[];
+#define xjd1Isspace(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x01)
+#define xjd1Isalnum(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x06)
+#define xjd1Isalpha(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x02)
+#define xjd1Isdigit(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x04)
+#define xjd1Isxdigit(x)  (xjd1CtypeMap[(unsigned char)(x)]&0x08)
+#define xjd1Isident(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x46)
+int xjd1RunParser(xjd1*, xjd1_stmt*, const char*, int*);
+void xjd1ParseError(Parse *, int, const char *, ...);
+
+/******************************** trace.c ************************************/
+const char *xjd1TokenName(int);
+void xjd1TraceCommand(String*,int,const Command*);
+void xjd1TraceQuery(String*,int,const Query*);
+void xjd1TraceDataSrc(String*,int,const DataSrc*);
+void xjd1TraceExpr(String*,const Expr*);
+void xjd1TraceExprList(String*,int, const ExprList*);
+
+/******************************** update.c ***********************************/
+int xjd1UpdateStep(xjd1_stmt*);
+
+/******************************** func.c *************************************/
+int xjd1FunctionInit(Expr *p, xjd1_stmt *pStmt, Query *pQuery, int bAggOk);
+JsonNode *xjd1FunctionEval(Expr *p);
+void xjd1FunctionClose(Expr *p);
+
+int xjd1AggregateInit(xjd1_stmt *, Query *, Expr *);
+int xjd1AggregateStep(Aggregate *, int *);
+int xjd1AggregateFinalize(Aggregate *);
+void xjd1AggregateClear(Query *);
+
+#endif /* _XJD1INT_H */