1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +03:00

initial check-in of the new version (CVS 1)

FossilOrigin-Name: 6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa
This commit is contained in:
drh
2000-05-29 14:26:00 +00:00
parent ce0da46e61
commit 75897234be
25 changed files with 15075 additions and 8 deletions

105
Makefile.in Normal file
View File

@ -0,0 +1,105 @@
#!/usr/make
#
# Makefile for SQLITE
# The toplevel directory of the source tree
#
TOP = @srcdir@
# C Compiler and options for use in building executables that
# will run on the platform that is doing the build.
#
BCC = @BUILD_CC@ @BUILD_CFLAGS@
# C Compile and options for use in building executables that
# will run on the target platform.
#
TCC = @TARGET_CC@ @TARGET_CFLAGS@ -I. -I${TOP}/src
# Tools used to build a static library.
#
AR = @TARGET_AR@
RANLIB = @TARGET_RANLIB@
# Compiler options needed for programs that use the GDBM library.
#
GDBM_FLAGS = @TARGET_GDBM_INC@
# The library that programs using GDBM must link against.
#
LIBGDBM = @TARGET_GDBM_LIBS@
# Compiler options needed for programs that use the readline() library.
#
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
# The library that programs using readline() must link against.
#
LIBREADLINE = @TARGET_READLINE_LIBS@
# Object files for the SQLite library.
#
LIBOBJ = build.o dbbe.o main.o parse.o tokenize.o util.o vdbe.o where.o
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
#
all: libsqlite.a sqlite
# libtclsqlite.a tclsqlite
libsqlite.a: $(LIBOBJ)
$(AR) libsqlite.a $(LIBOBJ)
$(RANLIB) libsqlite.a
sqlite: $(TOP)/src/shell.c libsqlite.a $(TOP)/src/sqlite.h
$(TCC) $(READLINE_FLAGS) -o sqlite $(TOP)/src/shell.c \
libsqlite.a $(LIBGDBM) $(LIBREADLINE)
# Rules to build the LEMON compiler generator
#
lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
$(BCC) -o lemon $(TOP)/tool/lemon.c
cp $(TOP)/tool/lempar.c .
# Header files used by all library source files.
#
HDR = \
$(TOP)/src/sqlite.h \
$(TOP)/src/sqliteInt.h \
$(TOP)/src/dbbe.h \
$(TOP)/src/vdbe.h \
parse.h
build.o: $(TOP)/src/build.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/build.c
dbbe.o: $(TOP)/src/dbbe.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
main.o: $(TOP)/src/main.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
parse.o: parse.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c parse.c
parse.h: parse.c
parse.c: $(TOP)/src/parse.y lemon
cp $(TOP)/src/parse.y .
./lemon parse.y
tokenize.o: $(TOP)/src/tokenize.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/tokenize.c
util.o: $(TOP)/src/util.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/util.c
vdbe.o: $(TOP)/src/vdbe.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/vdbe.c
where.o: $(TOP)/src/where.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/where.c
clean:
rm -f *.o sqlite libsqlite.a
rm -f lemon lempar.c parse.*

2006
configure vendored Executable file

File diff suppressed because it is too large Load Diff

513
configure.in Normal file
View File

@ -0,0 +1,513 @@
#
# This file describes a "configure" script that is used to build
# makefiles for a particular platform. Process this file using
# Autoconf version 1.13 in order to generate that script. All
# lines of this file up to the AC_INIT macro are ignored.
#
# The build process allows for using a cross-compiler. But the default
# action is to target the same platform that we are running on. The
# configure script needs to discover the following properties of the
# build and target systems:
#
# srcdir
#
# The is the name of the directory that contains the
# "configure" shell script. All source files are
# located relative to this directory.
#
# bindir
#
# The name of the directory where executables should be
# written by the "install" target of the makefile.
#
# program_prefix
#
# Add this prefix to the names of all executables that run
# on the target machine. Default: ""
#
# ENABLE_SHARED
#
# True if shared libraries should be generated.
#
# BUILD_CC
#
# The name of a command that is used to convert C
# source files into executables that run on the build
# platform.
#
# BUILD_CFLAGS
#
# Switches that the build compiler needs in order to construct
# command-line programs.
#
# BUILD_LIBS
#
# Libraries that the build compiler needs in order to construct
# command-line programs.
#
# BUILD_EXEEXT
#
# The filename extension for executables on the build
# platform. "" for Unix and ".exe" for Windows.
#
# TARGET_CC
#
# The name of a command that runs on the build platform
# and converts C source files into *.o files for the
# target platform. In other words, the cross-compiler.
#
# TARGET_CFLAGS
#
# Switches that the target compiler needs to turn C source files
# into *.o files. Do not include TARGET_TCL_INC in this list.
# Makefiles might add additional switches such as "-I.".
#
# TARGET_TCL_LIBS
#
# This is the library directives passed to the target linker
# that cause the executable to link against Tcl. This might
# be a switch like "-ltcl8.0" or pathnames of library file
# like "../../src/libtcl8.0.a".
#
# TARGET_TCL_INC
#
# This variables define the directory that contain header
# files for Tcl. If the compiler is able to find <tcl.h>
# on its own, then this can be blank.
#
# TARGET_GDBM_LIBS
#
# This is the library directives passed to the target linker
# that cause the executable to link against GDBM. This might
# be a switch like "-lgdbm" or pathnames of library file
# like "../../src/libgdbm.a".
#
# TARGET_GDBM_INC
#
# This variables define the directory that contain header
# files for GDBM. If the compiler is able to find <gdbm.h>
# on its own, then this can be blank.
#
# TARGET_READLINE_LIBS
#
# This is the library directives passed to the target linker
# that cause the executable to link against GDBM. This might
# be a switch like "-lreadline" or pathnames of library file
# like "../../src/libreadline.a".
#
# TARGET_READLINE_INC
#
# This variables define the directory that contain header
# files for the readline library. If the compiler is able
# to find <readline.h> on its own, then this can be blank.
#
# TARGET_LINK
#
# The name of the linker that combines *.o files generated
# by TARGET_CC into executables for the target platform.
#
# TARGET_LIBS
#
# Additional libraries or other switch that the target linker needs
# to build an executable on the target. Do not include
# on this list any libraries in TARGET_TCL_LIBS, TARGET_GDBM_LIBS,
# TARGET_READLINE_LIBS, etc.
#
# TARGET_EXEEXT
#
# The filename extension for executables on the
# target platform. "" for Unix and ".exe" for windows.
#
# The generated configure script will make an attempt to guess
# at all of the above parameters. You can override any of
# the guesses by setting the environment variable named
# "config_AAAA" where "AAAA" is the name of the parameter
# described above. (Exception: srcdir cannot be set this way.)
# If you have a file that sets one or more of these environment
# variables, you can invoke configure as follows:
#
# configure --with-hints=FILE
#
# where FILE is the name of the file that sets the environment
# variables. FILE should be an absolute pathname.
#
# If you have a Tcl/Tk/BLT source distribution available, then the
# files in that distribution will be used instead of any other
# Tcl/Tk/BLT files the script might discover if you tell the configure
# script about the source tree. Use commandline options:
#
# --with-tcl=PATH --with-tk=PATH --with-blt=PATH
#
# Or set environment variables config_WITH_TCL, config_WITH_TK, or
# config_WITH_BLT.
#
# This configure.in file is easy to reuse on other projects. Just
# change the argument to AC_INIT(). And disable any features that
# you don't need (for example BLT) by erasing or commenting out
# the corresponding code.
#
AC_INIT(src/sqlite.h)
dnl Put the RCS revision string after AC_INIT so that it will also
dnl show in in configure.
# The following RCS revision string applies to configure.in
# $Revision: 1.1 $
#########
# Make sure we are not building in a subdirectory of the source tree.
#
changequote(<<<,>>>)
temp=`echo $srcdir | grep '[^./]'`
changequote([,])
if test "$temp" = ""; then
AC_MSG_ERROR([
**************************************************************************
** This program may not be compiled in the same directory that contains **
** the configure script or any subdirectory of that directory. Rerun **
** the configure script from a directory that is separate from the **
** source tree. **
**************************************************************************])
fi
#########
# Set up an appropriate program prefix
#
if test "$program_prefix" = "NONE"; then
program_prefix=""
fi
AC_SUBST(program_prefix)
#########
# Check to see if the --with-hints=FILE option is used. If there is none,
# then check for a files named "$host.hints" and ../$hosts.hints where
# $host is the hostname of the build system. If still no hints are
# found, try looking in $system.hints and ../$system.hints where
# $system is the result of uname -s.
#
AC_ARG_WITH(hints,
[ --with-hints=FILE Read configuration options from FILE],
hints=$withval)
if test "$hints" = ""; then
host=`hostname | sed 's/\..*//'`
if test -r $host.hints; then
hints=$host.hints
else
if test -r ../$host.hints; then
hints=../$host.hints
fi
fi
fi
if test "$hints" = ""; then
sys=`uname -s`
if test -r $sys.hints; then
hints=$sys.hints
else
if test -r ../$sys.hints; then
hints=../$sys.hints
fi
fi
fi
if test "$hints" != ""; then
AC_MSG_RESULT(reading hints from $hints)
. $hints
fi
#########
# Locate a compiler for the build machine. This compiler should
# generate command-line programs that run on the build machine.
#
default_build_cflags="-g"
if test "$config_BUILD_CC" = ""; then
AC_PROG_CC
if test "$cross_compiling" = "yes"; then
AC_MSG_ERROR([unable to find a compiler for building build tools])
fi
BUILD_CC=$CC
default_build_cflags=$CFLAGS
else
BUILD_CC=$config_BUILD_CC
AC_MSG_CHECKING([host compiler])
CC=$BUILD_CC
AC_MSG_RESULT($BUILD_CC)
fi
AC_MSG_CHECKING([switches for the host compiler])
if test "$config_BUILD_CFLAGS" != ""; then
CFLAGS=$config_BUILD_CFLAGS
BUILD_CFLAGS=$config_BUILD_CFLAGS
else
BUILD_CFLAGS=$default_build_cflags
fi
AC_MSG_RESULT($BUILD_CFLAGS)
if test "$config_BUILD_LIBS" != ""; then
BUILD_LIBS=$config_BUILD_LIBS
fi
AC_SUBST(BUILD_CC)
AC_SUBST(BUILD_CFLAGS)
AC_SUBST(BUILD_LIBS)
##########
# Locate a compiler that converts C code into *.o files that run on
# the target machine.
#
AC_MSG_CHECKING([target compiler])
if test "$config_TARGET_CC" != ""; then
TARGET_CC=$config_TARGET_CC
else
TARGET_CC=$BUILD_CC
fi
AC_MSG_RESULT($TARGET_CC)
AC_MSG_CHECKING([switches on the target compiler])
if test "$config_TARGET_CFLAGS" != ""; then
TARGET_CFLAGS=$config_TARGET_CFLAGS
else
TARGET_CFLAGS=$BUILD_CFLAGS
fi
AC_MSG_RESULT($TARGET_CFLAGS)
AC_MSG_CHECKING([target linker])
if test "$config_TARGET_LINK" = ""; then
TARGET_LINK=$TARGET_CC
else
TARGET_LINK=$config_TARGET_LINK
fi
AC_MSG_RESULT($TARGET_LINK)
AC_MSG_CHECKING([switches on the target compiler])
if test "$config_TARGET_TFLAGS" != ""; then
TARGET_TFLAGS=$config_TARGET_TFLAGS
else
TARGET_TFLAGS=$BUILD_CFLAGS
fi
if test "$config_TARGET_RANLIB" != ""; then
TARGET_RANLIB=$config_TARGET_RANLIB
else
AC_PROG_RANLIB
TARGET_RANLIB=$RANLIB
fi
if test "$config_TARGET_AR" != ""; then
TARGET_RANLIB=$config_TARGET_AR
else
TARGET_AR='ar cr'
fi
AC_MSG_RESULT($TARGET_TFLAGS)
AC_SUBST(TARGET_CC)
AC_SUBST(TARGET_CFLAGS)
AC_SUBST(TARGET_LINK)
AC_SUBST(TARGET_LFLAGS)
AC_SUBST(TARGET_RANLIB)
AC_SUBST(TARGET_AR)
# Set the $cross variable if we are cross-compiling. Make
# it 0 if we are not.
#
AC_MSG_CHECKING([if host and target compilers are the same])
if test "$BUILD_CC" = "$TARGET_CC"; then
cross=0
AC_MSG_RESULT(yes)
else
cross=1
AC_MSG_RESULT(no)
fi
###########
# Lots of things are different if we are compiling for Windows using
# the CYGWIN environment. So check for that special case and handle
# things accordingly.
#
AC_MSG_CHECKING([if executables have the .exe suffix])
if test "$config_BUILD_EXEEXT" = ".exe"; then
CYGWIN=yes
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(unknown)
fi
if test "$CYGWIN" != "yes"; then
AC_CYGWIN
fi
if test "$CYGWIN" = "yes"; then
BUILD_EXEEXT=.exe
else
BUILD_EXEEXT=""
fi
if test "$cross" = "0"; then
TARGET_EXEEXT=$BUILD_EXEEXT
else
TARGET_EXEEXT=$config_TARGET_EXEEXT
fi
if test "$TARGET_EXEEXT" = ".exe"; then
OS_UNIX=0
OS_WIN=1
tclsubdir=win
else
OS_UNIX=1
OS_WIN=0
tclsubdir=unix
fi
TARGET_CFLAGS="$TARGET_CFLAGS -DOS_UNIX=$OS_UNIX -DOS_WIN=$OS_WIN"
AC_SUBST(BUILD_EXEEXT)
AC_SUBST(OS_UNIX)
AC_SUBST(OS_WIN)
AC_SUBST(TARGET_EXEEXT)
##########
# Extract generic linker options from the environment.
#
if test "$config_TARGET_LIBS" != ""; then
TARGET_LIBS=$config_TARGET_LIBS
else
TARGET_LIBS=""
fi
AC_SUBST(TARGET_LIBS)
##########
# Figure out what C libraries are required to compile Tcl programs.
#
if test "$config_TARGET_TCL_LIBS" != ""; then
TARGET_TCL_LIBS="$config_TARGET_TCL_LIBS"
else
if test "$with_tcl" != ""; then
extra=`echo $with_tcl/$tclsubdir/libtcl8*.a`
fi
CC=$TARGET_CC
AC_CHECK_FUNC(sin, LIBS="", LIBS="-lm")
AC_CHECK_LIB(dl, dlopen)
otherlibs=$LIBS
if test "$extra" != ""; then
LIBS=$extra
else
LIBS=""
AC_SEARCH_LIBS(Tcl_Init, dnl
tcl8.4 tcl8.3 tcl8.2 tcl8.1 tcl8.0 tcl80 tcl,,,$otherlibs)
fi
TARGET_TCL_LIBS="$LIBS $otherlibs"
fi
AC_SUBST(TARGET_TCL_LIBS)
##########
# Figure out where to get the TCL header files.
#
AC_MSG_CHECKING([TCL header files])
found=no
if test "$config_TARGET_TCL_INC" != ""; then
TARGET_TCL_INC=$config_TARGET_TCL_INC
found=yes
else
if test "$with_tcl" != ""; then
TARGET_TCL_INC="-I$with_tcl/generic -I$with_tcl/$tclsubdir"
found=yes
else
TARGET_TCL_INC=""
found=no
fi
fi
if test "$found" = "yes"; then
AC_MSG_RESULT($TARGET_TCL_INC)
else
AC_MSG_RESULT(not specified: still searching...)
AC_CHECK_HEADER(tcl.h, [found=yes])
fi
if test "$found" = "no"; then
for dir in /usr/local /usr/X11* /usr/pkg /usr/contrib /usr; do
AC_CHECK_FILE($dir/include/tcl.h, found=yes)
if test "$found" = "yes"; then
TARGET_TCL_INC="-I$dir/include"
break
fi
done
fi
AC_SUBST(TARGET_TCL_INC)
##########
# Figure out what C libraries are required to compile programs
# that use GDBM.
#
if test "$config_TARGET_GDBM_LIBS" != ""; then
TARGET_GDBM_LIBS="$config_TARGET_GDBM_LIBS"
else
CC=$TARGET_CC
LIBS=""
AC_SEARCH_LIBS(gdbm_open, gdbm,,,)
TARGET_GDBM_LIBS="$LIBS"
fi
AC_SUBST(TARGET_GDBM_LIBS)
##########
# Figure out where to get the GDBM header files.
#
AC_MSG_CHECKING([GDBM header files])
found=no
if test "$config_TARGET_GDBM_INC" != ""; then
TARGET_GDBM_INC=$config_TARGET_GDBM_INC
found=yes
fi
if test "$found" = "yes"; then
AC_MSG_RESULT($TARGET_TCL_INC)
else
AC_MSG_RESULT(not specified: still searching...)
AC_CHECK_HEADER(gdbm.h, [found=yes])
fi
if test "$found" = "no"; then
for dir in /usr/local /usr/pkg /usr/contrib; do
AC_CHECK_FILE($dir/include/gdbm.h, found=yes)
if test "$found" = "yes"; then
TARGET_GDBM_INC="-I$dir/include"
break
fi
done
fi
AC_SUBST(TARGET_GDBM_INC)
##########
# Figure out what C libraries are required to compile programs
# that use "readline()" library.
#
if test "$config_TARGET_READLINE_LIBS" != ""; then
TARGET_READLINE_LIBS="$config_TARGET_READLINE_LIBS"
else
CC=$TARGET_CC
LIBS=""
AC_SEARCH_LIBS(readline, readline,,,)
TARGET_READLINE_LIBS="$LIBS"
fi
AC_SUBST(TARGET_READLINE_LIBS)
##########
# Figure out where to get the READLINE header files.
#
AC_MSG_CHECKING([readline header files])
found=no
if test "$config_TARGET_READLINE_INC" != ""; then
TARGET_READLINE_INC=$config_TARGET_READLINE_INC
found=yes
fi
if test "$found" = "yes"; then
AC_MSG_RESULT($TARGET_READLINE_INC)
else
AC_MSG_RESULT(not specified: still searching...)
AC_CHECK_HEADER(readline.h, [found=yes])
fi
if test "$found" = "no"; then
for dir in /usr /usr/local /usr/local/readline /usr/contrib; do
AC_CHECK_FILE($dir/include/readline.h, found=yes)
if test "$found" = "yes"; then
TARGET_READLINE_INC="-I$dir/include"
break
fi
AC_CHECK_FILE($dir/include/readline/readline.h, found=yes)
if test "$found" = "yes"; then
TARGET_READLINE_INC="-I$dir/include/readline"
break
fi
done
fi
if test "$found" = "yes"; then
TARGET_HAVE_READLINE=1
else
TARGET_HAVE_READLINE=0
fi
AC_SUBST(TARGET_READLINE_INC)
AC_SUBST(TARGET_HAVE_READLINE)
#########
# Generate the output files.
#
AC_OUTPUT(Makefile)

861
doc/lemon.html Normal file
View File

@ -0,0 +1,861 @@
<html>
<head>
<title>The Lemon Parser Generator</title>
</head>
<body bgcolor=white>
<h1 align=center>The Lemon Parser Generator</h1>
<p>Lemon is an LALR(1) parser generator for C or C++.
It does the same job as ``bison'' and ``yacc''.
But lemon is not another bison or yacc clone. It
uses a different grammar syntax which is designed to
reduce the number of coding errors. Lemon also uses a more
sophisticated parsing engine that is faster than yacc and
bison and which is both reentrant and thread-safe.
Furthermore, Lemon implements features that can be used
to eliminate resource leaks, making is suitable for use
in long-running programs such as graphical user interfaces
or embedded controllers.</p>
<p>This document is an introduction to the Lemon
parser generator.</p>
<h2>Theory of Operation</h2>
<p>The main goal of Lemon is to translate a context free grammar (CFG)
for a particular language into C code that implements a parser for
that language.
The program has two inputs:
<ul>
<li>The grammar specification.
<li>A parser template file.
</ul>
Typically, only the grammar specification is supplied by the programmer.
Lemon comes with a default parser template which works fine for most
applications. But the user is free to substitute a different parser
template if desired.</p>
<p>Depending on command-line options, Lemon will generate between
one and three files of outputs.
<ul>
<li>C code to implement the parser.
<li>A header file defining an integer ID for each terminal symbol.
<li>An information file that describes the states of the generated parser
automaton.
</ul>
By default, all three of these output files are generated.
The header file is suppressed if the ``-m'' command-line option is
used and the report file is omitted when ``-q'' is selected.</p>
<p>The grammar specification file uses a ``.y'' suffix, by convention.
In the examples used in this document, we'll assume the name of the
grammar file is ``gram.y''. A typical use of Lemon would be the
following command:
<pre>
lemon gram.y
</pre>
This command will generate three output files named ``gram.c'',
``gram.h'' and ``gram.out''.
The first is C code to implement the parser. The second
is the header file that defines numerical values for all
terminal symbols, and the last is the report that explains
the states used by the parser automaton.</p>
<h3>Command Line Options</h3>
<p>The behavior of Lemon can be modified using command-line options.
You can obtain a list of the available command-line options together
with a brief explanation of what each does by typing
<pre>
lemon -?
</pre>
As of this writing, the following command-line options are supported:
<ul>
<li><tt>-b</tt>
<li><tt>-c</tt>
<li><tt>-g</tt>
<li><tt>-m</tt>
<li><tt>-q</tt>
<li><tt>-s</tt>
<li><tt>-x</tt>
</ul>
The ``-b'' option reduces the amount of text in the report file by
printing only the basis of each parser state, rather than the full
configuration.
The ``-c'' option suppresses action table compression. Using -c
will make the parser a little larger and slower but it will detect
syntax errors sooner.
The ``-g'' option causes no output files to be generated at all.
Instead, the input grammar file is printed on standard output but
with all comments, actions and other extraneous text deleted. This
is a useful way to get a quick summary of a grammar.
The ``-m'' option causes the output C source file to be compatible
with the ``makeheaders'' program.
Makeheaders is a program that automatically generates header files
from C source code. When the ``-m'' option is used, the header
file is not output since the makeheaders program will take care
of generated all header files automatically.
The ``-q'' option suppresses the report file.
Using ``-s'' causes a brief summary of parser statistics to be
printed. Like this:
<pre>
Parser statistics: 74 terminals, 70 nonterminals, 179 rules
340 states, 2026 parser table entries, 0 conflicts
</pre>
Finally, the ``-x'' option causes Lemon to print its version number
and copyright information
and then stop without attempting to read the grammar or generate a parser.</p>
<h3>The Parser Interface</h3>
<p>Lemon doesn't generate a complete, working program. It only generates
a few subroutines that implement a parser. This section describes
the interface to those subroutines. It is up to the programmer to
call these subroutines in an appropriate way in order to produce a
complete system.</p>
<p>Before a program begins using a Lemon-generated parser, the program
must first create the parser.
A new parser is created as follows:
<pre>
void *pParser = ParseAlloc( malloc );
</pre>
The ParseAlloc() routine allocates and initializes a new parser and
returns a pointer to it.
The actual data structure used to represent a parser is opaque --
its internal structure is not visible or usable by the calling routine.
For this reason, the ParseAlloc() routine returns a pointer to void
rather than a pointer to some particular structure.
The sole argument to the ParseAlloc() routine is a pointer to the
subroutine used to allocate memory. Typically this means ``malloc()''.</p>
<p>After a program is finished using a parser, it can reclaim all
memory allocated by that parser by calling
<pre>
ParseFree(pParser, free);
</pre>
The first argument is the same pointer returned by ParseAlloc(). The
second argument is a pointer to the function used to release bulk
memory back to the system.</p>
<p>After a parser has been allocated using ParseAlloc(), the programmer
must supply the parser with a sequence of tokens (terminal symbols) to
be parsed. This is accomplished by calling the following function
once for each token:
<pre>
Parse(pParser, hTokenID, sTokenData, pArg);
</pre>
The first argument to the Parse() routine is the pointer returned by
ParseAlloc().
The second argument is a small positive integer that tells the parse the
type of the next token in the data stream.
There is one token type for each terminal symbol in the grammar.
The gram.h file generated by Lemon contains #define statements that
map symbolic terminal symbol names into appropriate integer values.
(A value of 0 for the second argument is a special flag to the
parser to indicate that the end of input has been reached.)
The third argument is the value of the given token. By default,
the type of the third argument is integer, but the grammar will
usually redefine this type to be some kind of structure.
Typically the second argument will be a broad category of tokens
such as ``identifier'' or ``number'' and the third argument will
be the name of the identifier or the value of the number.</p>
<p>The Parse() function may have either three or four arguments,
depending on the grammar. If the grammar specification file request
it, the Parse() function will have a fourth parameter that can be
of any type chosen by the programmer. The parser doesn't do anything
with this argument except to pass it through to action routines.
This is a convenient mechanism for passing state information down
to the action routines without having to use global variables.</p>
<p>A typical use of a Lemon parser might look something like the
following:
<pre>
01 ParseTree *ParseFile(const char *zFilename){
02 Tokenizer *pTokenizer;
03 void *pParser;
04 Token sToken;
05 int hTokenId;
06 ParserState sState;
07
08 pTokenizer = TokenizerCreate(zFilename);
09 pParser = ParseAlloc( malloc );
10 InitParserState(&sState);
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
12 Parse(pParser, hTokenId, sToken, &sState);
13 }
14 Parse(pParser, 0, sToken, &sState);
15 ParseFree(pParser, free );
16 TokenizerFree(pTokenizer);
17 return sState.treeRoot;
18 }
</pre>
This example shows a user-written routine that parses a file of
text and returns a pointer to the parse tree.
(We've omitted all error-handling from this example to keep it
simple.)
We assume the existence of some kind of tokenizer which is created
using TokenizerCreate() on line 8 and deleted by TokenizerFree()
on line 16. The GetNextToken() function on line 11 retrieves the
next token from the input file and puts its type in the
integer variable hTokenId. The sToken variable is assumed to be
some kind of structure that contains details about each token,
such as its complete text, what line it occurs on, etc. </p>
<p>This example also assumes the existence of structure of type
ParserState that holds state information about a particular parse.
An instance of such a structure is created on line 6 and initialized
on line 10. A pointer to this structure is passed into the Parse()
routine as the optional 4th argument.
The action routine specified by the grammar for the parser can use
the ParserState structure to hold whatever information is useful and
appropriate. In the example, we note that the treeRoot field of
the ParserState structure is left pointing to the root of the parse
tree.</p>
<p>The core of this example as it relates to Lemon is as follows:
<pre>
ParseFile(){
pParser = ParseAlloc( malloc );
while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
Parse(pParser, hTokenId, sToken);
}
Parse(pParser, 0, sToken);
ParseFree(pParser, free );
}
</pre>
Basically, what a program has to do to use a Lemon-generated parser
is first create the parser, then send it lots of tokens obtained by
tokenizing an input source. When the end of input is reached, the
Parse() routine should be called one last time with a token type
of 0. This step is necessary to inform the parser that the end of
input has been reached. Finally, we reclaim memory used by the
parser by calling ParseFree().</p>
<p>There is one other interface routine that should be mentioned
before we move on.
The ParseTrace() function can be used to generate debugging output
from the parser. A prototype for this routine is as follows:
<pre>
ParseTrace(FILE *stream, char *zPrefix);
</pre>
After this routine is called, a short (one-line) message is written
to the designated output stream every time the parser changes states
or calls an action routine. Each such message is prefaced using
the text given by zPrefix. This debugging output can be turned off
by calling ParseTrace() again with a first argument of NULL (0).</p>
<h3>Differences With YACC and BISON</h3>
<p>Programmers who have previously used the yacc or bison parser
generator will notice several important differences between yacc and/or
bison and Lemon.
<ul>
<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
the tokenizer calls the parser.
<li>Lemon uses no global variables. Yacc and bison use global variables
to pass information between the tokenizer and parser.
<li>Lemon allows multiple parsers to be running simultaneously. Yacc
and bison do not.
</ul>
These differences may cause some initial confusion for programmers
with prior yacc and bison experience.
But after years of experience using Lemon, I firmly
believe that the Lemon way of doing things is better.</p>
<h2>Input File Syntax</h2>
<p>The main purpose of the grammar specification file for Lemon is
to define the grammar for the parser. But the input file also
specifies additional information Lemon requires to do its job.
Most of the work in using Lemon is in writing an appropriate
grammar file.</p>
<p>The grammar file for lemon is, for the most part, free format.
It does not have sections or divisions like yacc or bison. Any
declaration can occur at any point in the file.
Lemon ignores whitespace (except where it is needed to separate
tokens) and it honors the same commenting conventions as C and C++.</p>
<h3>Terminals and Nonterminals</h3>
<p>A terminal symbol (token) is any string of alphanumeric
and underscore characters
that begins with an upper case letter.
A terminal can contain lower class letters after the first character,
but the usual convention is to make terminals all upper case.
A nonterminal, on the other hand, is any string of alphanumeric
and underscore characters than begins with a lower case letter.
Again, the usual convention is to make nonterminals use all lower
case letters.</p>
<p>In Lemon, terminal and nonterminal symbols do not need to
be declared or identified in a separate section of the grammar file.
Lemon is able to generate a list of all terminals and nonterminals
by examining the grammar rules, and it can always distinguish a
terminal from a nonterminal by checking the case of the first
character of the name.</p>
<p>Yacc and bison allow terminal symbols to have either alphanumeric
names or to be individual characters included in single quotes, like
this: ')' or '$'. Lemon does not allow this alternative form for
terminal symbols. With Lemon, all symbols, terminals and nonterminals,
must have alphanumeric names.</p>
<h3>Grammar Rules</h3>
<p>The main component of a Lemon grammar file is a sequence of grammar
rules.
Each grammar rule consists of a nonterminal symbol followed by
the special symbol ``::='' and then a list of terminals and/or nonterminals.
The rule is terminated by a period.
The list of terminals and nonterminals on the right-hand side of the
rule can be empty.
Rules can occur in any order, except that the left-hand side of the
first rule is assumed to be the start symbol for the grammar (unless
specified otherwise using the <tt>%start</tt> directive described below.)
A typical sequence of grammar rules might look something like this:
<pre>
expr ::= expr PLUS expr.
expr ::= expr TIMES expr.
expr ::= LPAREN expr RPAREN.
expr ::= VALUE.
</pre>
</p>
<p>There is one non-terminal in this example, ``expr'', and five
terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
``RPAREN'' and ``VALUE''.</p>
<p>Like yacc and bison, Lemon allows the grammar to specify a block
of C code that will be executed whenever a grammar rule is reduced
by the parser.
In Lemon, this action is specified by putting the C code (contained
within curly braces <tt>{...}</tt>) immediately after the
period that closes the rule.
For example:
<pre>
expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
</pre>
</p>
<p>In order to be useful, grammar actions must normally be linked to
their associated grammar rules.
In yacc and bison, this is accomplished by embedding a ``$$'' in the
action to stand for the value of the left-hand side of the rule and
symbols ``$1'', ``$2'', and so forth to stand for the value of
the terminal or nonterminal at position 1, 2 and so forth on the
right-hand side of the rule.
This idea is very powerful, but it is also very error-prone. The
single most common source of errors in a yacc or bison grammar is
to miscount the number of symbols on the right-hand side of a grammar
rule and say ``$7'' when you really mean ``$8''.</p>
<p>Lemon avoids the need to count grammar symbols by assigning symbolic
names to each symbol in a grammar rule and then using those symbolic
names in the action.
In yacc or bison, one would write this:
<pre>
expr -> expr PLUS expr { $$ = $1 + $3; };
</pre>
But in Lemon, the same rule becomes the following:
<pre>
expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
</pre>
In the Lemon rule, any symbol in parentheses after a grammar rule
symbol becomes a place holder for that symbol in the grammar rule.
This place holder can then be used in the associated C action to
stand for the value of that symbol.<p>
<p>The Lemon notation for linking a grammar rule with its reduce
action is superior to yacc/bison on several counts.
First, as mentioned above, the Lemon method avoids the need to
count grammar symbols.
Secondly, if a terminal or nonterminal in a Lemon grammar rule
includes a linking symbol in parentheses but that linking symbol
is not actually used in the reduce action, then an error message
is generated.
For example, the rule
<pre>
expr(A) ::= expr(B) PLUS expr(C). { A = B; }
</pre>
will generate an error because the linking symbol ``C'' is used
in the grammar rule but not in the reduce action.</p>
<p>The Lemon notation for linking grammar rules to reduce actions
also facilitates the use of destructors for reclaiming memory
allocated by the values of terminals and nonterminals on the
right-hand side of a rule.</p>
<h3>Precedence Rules</h3>
<p>Lemon resolves parsing ambiguities in exactly the same way as
yacc and bison. A shift-reduce conflict is resolved in favor
of the shift, and a reduce-reduce conflict is resolved by reducing
whichever rule comes first in the grammar file.</p>
<p>Just like in
yacc and bison, Lemon allows a measure of control
over the resolution of paring conflicts using precedence rules.
A precedence value can be assigned to any terminal symbol
using the %left, %right or %nonassoc directives. Terminal symbols
mentioned in earlier directives have a lower precedence that
terminal symbols mentioned in later directives. For example:</p>
<p><pre>
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
</pre></p>
<p>In the preceding sequence of directives, the AND operator is
defined to have the lowest precedence. The OR operator is one
precedence level higher. And so forth. Hence, the grammar would
attempt to group the ambiguous expression
<pre>
a AND b OR c
</pre>
like this
<pre>
a AND (b OR c).
</pre>
The associativity (left, right or nonassoc) is used to determine
the grouping when the precedence is the same. AND is left-associative
in our example, so
<pre>
a AND b AND c
</pre>
is parsed like this
<pre>
(a AND b) AND c.
</pre>
The EXP operator is right-associative, though, so
<pre>
a EXP b EXP c
</pre>
is parsed like this
<pre>
a EXP (b EXP c).
</pre>
The nonassoc precedence is used for non-associative operators.
So
<pre>
a EQ b EQ c
</pre>
is an error.</p>
<p>The precedence of non-terminals is transferred to rules as follows:
The precedence of a grammar rule is equal to the precedence of the
left-most terminal symbol in the rule for which a precedence is
defined. This is normally what you want, but in those cases where
you want to precedence of a grammar rule to be something different,
you can specify an alternative precedence symbol by putting the
symbol in square braces after the period at the end of the rule and
before any C-code. For example:</p>
<p><pre>
expr = MINUS expr. [NOT]
</pre></p>
<p>This rule has a precedence equal to that of the NOT symbol, not the
MINUS symbol as would have been the case by default.</p>
<p>With the knowledge of how precedence is assigned to terminal
symbols and individual
grammar rules, we can now explain precisely how parsing conflicts
are resolved in Lemon. Shift-reduce conflicts are resolved
as follows:
<ul>
<li> If either the token to be shifted or the rule to be reduced
lacks precedence information, then resolve in favor of the
shift, but report a parsing conflict.
<li> If the precedence of the token to be shifted is greater than
the precedence of the rule to reduce, then resolve in favor
of the shift. No parsing conflict is reported.
<li> If the precedence of the token it be shifted is less than the
precedence of the rule to reduce, then resolve in favor of the
reduce action. No parsing conflict is reported.
<li> If the precedences are the same and the shift token is
right-associative, then resolve in favor of the shift.
No parsing conflict is reported.
<li> If the precedences are the same the the shift token is
left-associative, then resolve in favor of the reduce.
No parsing conflict is reported.
<li> Otherwise, resolve the conflict by doing the shift and
report the parsing conflict.
</ul>
Reduce-reduce conflicts are resolved this way:
<ul>
<li> If either reduce rule
lacks precedence information, then resolve in favor of the
rule that appears first in the grammar and report a parsing
conflict.
<li> If both rules have precedence and the precedence is different
then resolve the dispute in favor of the rule with the highest
precedence and do not report a conflict.
<li> Otherwise, resolve the conflict by reducing by the rule that
appears first in the grammar and report a parsing conflict.
</ul>
<h3>Special Directives</h3>
<p>The input grammar to Lemon consists of grammar rules and special
directives. We've described all the grammar rules, so now we'll
talk about the special directives.</p>
<p>Directives in lemon can occur in any order. You can put them before
the grammar rules, or after the grammar rules, or in the mist of the
grammar rules. It doesn't matter. The relative order of
directives used to assign precedence to terminals is important, but
other than that, the order of directives in Lemon is arbitrary.</p>
<p>Lemon supports the following special directives:
<ul>
<li><tt>%destructor</tt>
<li><tt>%extra_argument</tt>
<li><tt>%include</tt>
<li><tt>%left</tt>
<li><tt>%name</tt>
<li><tt>%nonassoc</tt>
<li><tt>%parse_accept</tt>
<li><tt>%parse_failure </tt>
<li><tt>%right</tt>
<li><tt>%stack_overflow</tt>
<li><tt>%stack_size</tt>
<li><tt>%start_symbol</tt>
<li><tt>%syntax_error</tt>
<li><tt>%token_destructor</tt>
<li><tt>%token_prefix</tt>
<li><tt>%token_type</tt>
<li><tt>%type</tt>
</ul>
Each of these directives will be described separately in the
following sections:</p>
<h4>The <tt>%destructor</tt> directive</h4>
<p>The %destructor directive is used to specify a destructor for
a non-terminal symbol.
(See also the %token_destructor directive which is used to
specify a destructor for terminal symbols.)</p>
<p>A non-terminal's destructor is called to dispose of the
non-terminal's value whenever the non-terminal is popped from
the stack. This includes all of the following circumstances:
<ul>
<li> When a rule reduces and the value of a non-terminal on
the right-hand side is not linked to C code.
<li> When the stack is popped during error processing.
<li> When the ParseFree() function runs.
</ul>
The destructor can do whatever it wants with the value of
the non-terminal, but its design is to deallocate memory
or other resources held by that non-terminal.</p>
<p>Consider an example:
<pre>
%type nt {void*}
%destructor nt { free($$); }
nt(A) ::= ID NUM. { A = malloc( 100 ); }
</pre>
This example is a bit contrived but it serves to illustrate how
destructors work. The example shows a non-terminal named
``nt'' that holds values of type ``void*''. When the rule for
an ``nt'' reduces, it sets the value of the non-terminal to
space obtained from malloc(). Later, when the nt non-terminal
is popped from the stack, the destructor will fire and call
free() on this malloced space, thus avoiding a memory leak.
(Note that the symbol ``$$'' in the destructor code is replaced
by the value of the non-terminal.)</p>
<p>It is important to note that the value of a non-terminal is passed
to the destructor whenever the non-terminal is removed from the
stack, unless the non-terminal is used in a C-code action. If
the non-terminal is used by C-code, then it is assumed that the
C-code will take care of destroying it if it should really
be destroyed. More commonly, the value is used to build some
larger structure and we don't want to destroy it, which is why
the destructor is not called in this circumstance.</p>
<p>By appropriate use of destructors, it is possible to
build a parser using Lemon that can be used within a long-running
program, such as a GUI, that will not leak memory or other resources.
To do the same using yacc or bison is much more difficult.</p>
<h4>The <tt>%extra_argument</tt> directive</h4>
The %extra_argument directive instructs Lemon to add a 4th parameter
to the parameter list of the Parse() function it generates. Lemon
doesn't do anything itself with this extra argument, but it does
make the argument available to C-code action routines, destructors,
and so forth. For example, if the grammar file contains:</p>
<p><pre>
%extra_argument { MyStruct *pAbc }
</pre></p>
<p>Then the Parse() function generated will have an 4th parameter
of type ``MyStruct*'' and all action routines will have access to
a variable named ``pAbc'' that is the value of the 4th parameter
in the most recent call to Parse().</p>
<h4>The <tt>%include</tt> directive</h4>
<p>The %include directive specifies C code that is included at the
top of the generated parser. You can include any text you want --
the Lemon parser generator copies to blindly. If you have multiple
%include directives in your grammar file, their values are concatenated
before being put at the beginning of the generated parser.</p>
<p>The %include directive is very handy for getting some extra #include
preprocessor statements at the beginning of the generated parser.
For example:</p>
<p><pre>
%include {#include &lt;unistd.h&gt;}
</pre></p>
<p>This might be needed, for example, if some of the C actions in the
grammar call functions that are prototyed in unistd.h.</p>
<h4>The <tt>%left</tt> directive</h4>
The %left directive is used (along with the %right and
%nonassoc directives) to declare precedences of terminal
symbols. Every terminal symbol whose name appears after
a %left directive but before the next period (``.'') is
given the same left-associative precedence value. Subsequent
%left directives have higher precedence. For example:</p>
<p><pre>
%left AND.
%left OR.
%nonassoc EQ NE GT GE LT LE.
%left PLUS MINUS.
%left TIMES DIVIDE MOD.
%right EXP NOT.
</pre></p>
<p>Note the period that terminates each %left, %right or %nonassoc
directive.</p>
<p>LALR(1) grammars can get into a situation where they require
a large amount of stack space if you make heavy use or right-associative
operators. For this reason, it is recommended that you use %left
rather than %right whenever possible.</p>
<h4>The <tt>%name</tt> directive</h4>
<p>By default, the functions generated by Lemon all begin with the
five-character string ``Parse''. You can change this string to something
different using the %name directive. For instance:</p>
<p><pre>
%name Abcde
</pre></p>
<p>Putting this directive in the grammar file will cause Lemon to generate
functions named
<ul>
<li> AbcdeAlloc(),
<li> AbcdeFree(),
<li> AbcdeTrace(), and
<li> Abcde().
</ul>
The %name directive allows you to generator two or more different
parsers and link them all into the same executable.
</p>
<h4>The <tt>%nonassoc</tt> directive</h4>
<p>This directive is used to assign non-associative precedence to
one or more terminal symbols. See the section on precedence rules
or on the %left directive for additional information.</p>
<h4>The <tt>%parse_accept</tt> directive</h4>
<p>The %parse_accept directive specifies a block of C code that is
executed whenever the parser accepts its input string. To ``accept''
an input string means that the parser was able to process all tokens
without error.</p>
<p>For example:</p>
<p><pre>
%parse_accept {
printf("parsing complete!\n");
}
</pre></p>
<h4>The <tt>%parse_failure</tt> directive</h4>
<p>The %parse_failure directive specifies a block of C code that
is executed whenever the parser fails complete. This code is not
executed until the parser has tried and failed to resolve an input
error using is usual error recovery strategy. The routine is
only invoked when parsing is unable to continue.</p>
<p><pre>
%parse_failure {
fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
}
</pre></p>
<h4>The <tt>%right</tt> directive</h4>
<p>This directive is used to assign right-associative precedence to
one or more terminal symbols. See the section on precedence rules
or on the %left directive for additional information.</p>
<h4>The <tt>%stack_overflow</tt> directive</h4>
<p>The %stack_overflow directive specifies a block of C code that
is executed if the parser's internal stack ever overflows. Typically
this just prints an error message. After a stack overflow, the parser
will be unable to continue and must be reset.</p>
<p><pre>
%stack_overflow {
fprintf(stderr,"Giving up. Parser stack overflow\n");
}
</pre></p>
<p>You can help prevent parser stack overflows by avoiding the use
of right recursion and right-precedence operators in your grammar.
Use left recursion and and left-precedence operators instead, to
encourage rules to reduce sooner and keep the stack size down.
For example, do rules like this:
<pre>
list ::= list element. // left-recursion. Good!
list ::= .
</pre>
Not like this:
<pre>
list ::= element list. // right-recursion. Bad!
list ::= .
</pre>
<h4>The <tt>%stack_size</tt> directive</h4>
<p>If stack overflow is a problem and you can't resolve the trouble
by using left-recursion, then you might want to increase the size
of the parser's stack using this directive. Put an positive integer
after the %stack_size directive and Lemon will generate a parse
with a stack of the requested size. The default value is 100.</p>
<p><pre>
%stack_size 2000
</pre></p>
<h4>The <tt>%start_symbol</tt> directive</h4>
<p>By default, the start-symbol for the grammar that Lemon generates
is the first non-terminal that appears in the grammar file. But you
can choose a different start-symbol using the %start_symbol directive.</p>
<p><pre>
%start_symbol prog
</pre></p>
<h4>The <tt>%token_destructor</tt> directive</h4>
<p>The %destructor directive assigns a destructor to a non-terminal
symbol. (See the description of the %destructor directive above.)
This directive does the same thing for all terminal symbols.</p>
<p>Unlike non-terminal symbols which may each have a different data type
for their values, terminals all use the same data type (defined by
the %token_type directive) and so they use a common destructor. Other
than that, the token destructor works just like the non-terminal
destructors.</p>
<h4>The <tt>%token_prefix</tt> directive</h4>
<p>Lemon generates #defines that assign small integer constants
to each terminal symbol in the grammar. If desired, Lemon will
add a prefix specified by this directive
to each of the #defines it generates.
So if the default output of Lemon looked like this:
<pre>
#define AND 1
#define MINUS 2
#define OR 3
#define PLUS 4
</pre>
You can insert a statement into the grammar like this:
<pre>
%token_prefix TOKEN_
</pre>
to cause Lemon to produce these symbols instead:
<pre>
#define TOKEN_AND 1
#define TOKEN_MINUS 2
#define TOKEN_OR 3
#define TOKEN_PLUS 4
</pre>
<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
<p>These directives are used to specify the data types for values
on the parser's stack associated with terminal and non-terminal
symbols. The values of all terminal symbols must be of the same
type. This turns out to be the same data type as the 3rd parameter
to the Parse() function generated by Lemon. Typically, you will
make the value of a terminal symbol by a pointer to some kind of
token structure. Like this:</p>
<p><pre>
%token_type {Token*}
</pre></p>
<p>If the data type of terminals is not specified, the default value
is ``int''.</p>
<p>Non-terminal symbols can each have their own data types. Typically
the data type of a non-terminal is a pointer to the root of a parse-tree
structure that contains all information about that non-terminal.
For example:</p>
<p><pre>
%type expr {Expr*}
</pre></p>
<p>Each entry on the parser's stack is actually a union containing
instances of all data types for every non-terminal and terminal symbol.
Lemon will automatically use the correct element of this union depending
on what the corresponding non-terminal or terminal symbol is. But
the grammar designer should keep in mind that the size of the union
will be the size of its largest element. So if you have a single
non-terminal whose data type requires 1K of storage, then your 100
entry parser stack will require 100K of heap space. If you are willing
and able to pay that price, fine. You just need to know.</p>
<h3>Error Processing</h3>
<p>After extensive experimentation over several years, it has been
discovered that the error recovery strategy used by yacc is about
as good as it gets. And so that is what Lemon uses.</p>
<p>When a Lemon-generated parser encounters a syntax error, it
first invokes the code specified by the %syntax_error directive, if
any. It then enters its error recovery strategy. The error recovery
strategy is to begin popping the parsers stack until it enters a
state where it is permitted to shift a special non-terminal symbol
named ``error''. It then shifts this non-terminal and continues
parsing. But the %syntax_error routine will not be called again
until at least three new tokens have been successfully shifted.</p>
<p>If the parser pops its stack until the stack is empty, and it still
is unable to shift the error symbol, then the %parse_failed routine
is invoked and the parser resets itself to its start state, ready
to begin parsing a new file. This is what will happen at the very
first syntax error, of course, if there are no instances of the
``error'' non-terminal in your grammar.</p>
</body>
</html>

View File

@ -1,8 +1,29 @@
C initial\sempty\scheck-in C initial\scheck-in\sof\sthe\snew\sversion\s(CVS\s1)
D 2000-05-29T14:16:00 D 2000-05-29T14:26:00
P F Makefile.in 4bd5c67a3a2816e930df4b22df8c1631ee87ff0c
R d41d8cd98f00b204e9800998ecf8427e F configure 8faba4d0194321e5f61a64e842c65eab0f68e6d8 x
T *branch * trunk F configure.in 4fc2947d631037bd340b112d6193847d7ac827ae
T *sym-trunk * F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F src/build.c 64016990ebbbcbc848165551732a1f9f397bd150
F src/dbbe.c ab05293e89525041eaab8b4aca10516db3648792
F src/dbbe.h bedeb3a0985bb584458e7849fb59927e99e751e6
F src/main.c 25cce7bce0eb3ba10bada7c05f4b38dc6dbbc86f
F src/shell.c de770d16aae1ea219e7a9ab5147a4437c23b4a6c
F src/sqlite.h 2397c17a8f4ca90c09acab0100dc7e2f8f441b69
F src/sqliteInt.h 0365970442441b5e9b74e1e828afdeac7b0662be
F src/tclsqlite.c 6eca1c1b8713048245d295ed18dca71c91d4931f
F src/tokenize.c ab578d90ec6ab117b7ade6e6cfbcb5b0f9cad500
F src/util.c 370c2339bb9ff82645804a4c62506149392fd032
F src/vdbe.c 80132b6bb9a744d1990a1c16666d54baaff2dbc3
F src/vdbe.h e721ad308f2e6ca805cebc4dd0a196ce4419d030
F src/where.c 67ffea57920e16b33c580e9a9b9855b3ec9dea7b
F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b
F tool/lemon.c cff35578b3c4d1491021b6418016639ebe21b1a5
F tool/lempar.c a1eec94d6eacc12332368660ec65f3b248853833
F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
P 704b122e5308587b60b47a5c2fff40c593d4bf8f
R 33c985d67f2f41286bc65b8529a1ae84
U drh U drh
Z 8c6f780fffd15dac29a44b424067ccfc Z a9e2b0f2d67c72179e4ea5172821c6d6

View File

@ -1 +1 @@
704b122e5308587b60b47a5c2fff40c593d4bf8f 6f3655f79f9b6fc9fb7baaa10a7e0f2b6a512dfa

1435
src/build.c Normal file

File diff suppressed because it is too large Load Diff

510
src/dbbe.c Normal file
View File

@ -0,0 +1,510 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code to implement the database baseend (DBBE)
** for sqlite. The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses GDBM as the database backend. It should be
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
** $Id: dbbe.c,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
/*
** Each open database file is an instance of this structure.
*/
typedef struct BeFile BeFile;
struct BeFile {
char *zName; /* Name of the file */
GDBM_FILE dbf; /* The file itself */
int nRef; /* Number of references */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The complete database is an instance of the following structure.
*/
struct Dbbe {
char *zDir; /* The directory containing the database */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
};
/*
** Each file within the database is an instance of this
** structure.
*/
struct DbbeTable {
Dbbe *pBe; /* The database of which this record is a part */
BeFile *pFile; /* The database file for this table */
datum key; /* Most recently used key */
datum data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** This routine opens a new database. For the current driver scheme,
** the database name is the name of the directory
** containing all the files of the database.
*/
Dbbe *sqliteDbbeOpen(
const char *zName, /* The name of the database */
int write, /* True if we will be writing to the database */
int create, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbe *pNew;
struct stat statbuf;
if( stat(zName, &statbuf)!=0 ){
sqliteSetString(pzErrMsg, "can't find file \"", zName, "\"", 0);
return 0;
}
if( !S_ISDIR(statbuf.st_mode) ){
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
return 0;
}
pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1);
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = write;
pNew->pOpen = 0;
return pNew;
}
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
void sqliteDbbeClose(Dbbe *pBe){
BeFile *pFile, *pNext;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
gdbm_close(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Translate the name of a table into the name of a file that holds
** that table. Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
char *zFile = 0;
int i;
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
if( zFile==0 ) return 0;
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
int c = zFile[i];
if( isupper(c) ){
zFile[i] = tolower(c);
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
zFile[i] = '+';
}
}
return zFile;
}
/*
** Open a new table cursor
*/
DbbeTable *sqliteDbbeOpenTable(
Dbbe *pBe, /* The database the table belongs to */
const char *zTable, /* The name of the table */
int writeable /* True to open for writing */
){
char *zFile; /* Name of the table file */
DbbeTable *pTable; /* The new table cursor */
BeFile *pFile; /* The underlying data file for this table */
pTable = sqliteMalloc( sizeof(*pTable) );
if( pTable==0 ) return 0;
zFile = sqliteFileOfTable(pBe, zTable);
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
if( strcmp(pFile->zName,zFile)==0 ) break;
}
if( pFile==0 ){
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return 0;
}
pFile->zName = zFile;
pFile->nRef = 1;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
}
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
pFile->dbf = gdbm_open(pFile->zName, 0, GDBM_WRCREAT, 0640, 0);
}else{
sqliteFree(zFile);
pFile->nRef++;
}
pTable->pBe = pBe;
pTable->pFile = pFile;
pTable->readPending = 0;
pTable->needRewind = 1;
return pTable;
}
/*
** Drop a table from the database.
*/
void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
char *zFile; /* Name of the table file */
zFile = sqliteFileOfTable(pBe, zTable);
unlink(zFile);
sqliteFree(zFile);
}
/*
** Close a table previously opened by sqliteDbbeOpenTable().
*/
void sqliteDbbeCloseTable(DbbeTable *pTable){
BeFile *pFile;
Dbbe *pBe;
if( pTable==0 ) return;
pFile = pTable->pFile;
pBe = pTable->pBe;
pFile->nRef--;
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
gdbm_close(pFile->dbf);
}
if( pFile->pPrev ){
pFile->pPrev->pNext = pFile->pNext;
}else{
pBe->pOpen = pFile->pNext;
}
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
if( pTable->key.dptr ) free(pTable->key.dptr);
if( pTable->data.dptr ) free(pTable->data.dptr);
memset(pTable, 0, sizeof(*pTable));
sqliteFree(pTable);
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr);
p->dptr = 0;
p->dsize = 0;
}
/*
** Fetch a single record from an open table. Return 1 on success
** and 0 on failure.
*/
int sqliteDbbeFetch(DbbeTable *pTable, int nKey, char *pKey){
datum key;
key.dsize = nKey;
key.dptr = pKey;
datumClear(&pTable->key);
datumClear(&pTable->data);
if( pTable->pFile && pTable->pFile->dbf ){
pTable->data = gdbm_fetch(pTable->pFile->dbf, key);
}
return pTable->data.dptr!=0;
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
int sqliteDbbeCopyKey(DbbeTable *pTable, int offset, int size, char *zBuf){
int n;
if( offset>=pTable->key.dsize ) return 0;
if( offset+size>pTable->key.dsize ){
n = pTable->key.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pTable->key.dptr[offset], n);
return n;
}
int sqliteDbbeCopyData(DbbeTable *pTable, int offset, int size, char *zBuf){
int n;
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
pTable->readPending = 0;
}
if( offset>=pTable->data.dsize ) return 0;
if( offset+size>pTable->data.dsize ){
n = pTable->data.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pTable->data.dptr[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
char *sqliteDbbeReadKey(DbbeTable *pTable, int offset){
if( offset<0 || offset>=pTable->key.dsize ) return "";
return &pTable->key.dptr[offset];
}
char *sqliteDbbeReadData(DbbeTable *pTable, int offset){
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
pTable->readPending = 0;
}
if( offset<0 || offset>=pTable->data.dsize ) return "";
return &pTable->data.dptr[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
int sqliteDbbeKeyLength(DbbeTable *pTable){
return pTable->key.dsize;
}
int sqliteDbbeDataLength(DbbeTable *pTable){
if( pTable->readPending && pTable->pFile && pTable->pFile->dbf ){
pTable->data = gdbm_fetch(pTable->pFile->dbf, pTable->key);
pTable->readPending = 0;
}
return pTable->data.dsize;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
int sqliteDbbeRewind(DbbeTable *pTable){
pTable->needRewind = 1;
return 0;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
int sqliteDbbeNextKey(DbbeTable *pTable){
datum nextkey;
int rc;
if( pTable==0 || pTable->pFile==0 || pTable->pFile->dbf==0 ){
pTable->readPending = 0;
return 0;
}
if( pTable->needRewind ){
nextkey = gdbm_firstkey(pTable->pFile->dbf);
pTable->needRewind = 0;
}else{
nextkey = gdbm_nextkey(pTable->pFile->dbf, pTable->key);
}
datumClear(&pTable->key);
datumClear(&pTable->data);
pTable->key = nextkey;
if( pTable->key.dptr ){
pTable->readPending = 1;
rc = 1;
}else{
pTable->needRewind = 1;
pTable->readPending = 0;
rc = 0;
}
return rc;
}
/*
** The following are state variables for the RC4 algorithm. We
** use RC4 as a random number generator. Each call to RC4 gives
** a random 8-bit number.
*/
static struct {
int i, j;
int s[256];
} rc4;
/*
** Initialize the RC4 algorithm.
*/
static void rc4init(char *key, int keylen){
int i;
char k[256];
rc4.j = 0;
rc4.i = 0;
for(i=0; i<256; i++){
rc4.s[i] = i;
k[i] = key[i%keylen];
}
for(i=0; i<256; i++){
int t;
rc4.j = (rc4.j + rc4.s[i] + k[i]) & 0xff;
t = rc4.s[rc4.j];
rc4.s[rc4.j] = rc4.s[i];
rc4.s[i] = t;
}
}
/*
** Get a single 8-bit random value from the RC4 algorithm.
*/
static int rc4byte(void){
int t;
rc4.i = (rc4.i + 1) & 0xff;
rc4.j = (rc4.j + rc4.s[rc4.i]) & 0xff;
t = rc4.s[rc4.i];
rc4.s[rc4.i] = rc4.s[rc4.j];
rc4.s[rc4.j] = t;
t = rc4.s[rc4.i] + rc4.s[rc4.j];
return t & 0xff;
}
/*
** Get a new integer key.
*/
int sqliteDbbeNew(DbbeTable *pTable){
static int isInit = 0;
int iKey;
datum key;
int go = 1;
int i;
if( !isInit ){
struct stat statbuf;
stat(pTable->pFile->zName, &statbuf);
time(&statbuf.st_ctime);
rc4init((char*)&statbuf, sizeof(statbuf));
isInit = 1;
}
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 1;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
iKey = (iKey<<8) + rc4byte();
}
key.dptr = (char*)&iKey;
key.dsize = 4;
go = gdbm_exists(pTable->pFile->dbf, key);
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
int sqliteDbbePut(DbbeTable *pTable, int nKey,char *pKey,int nData,char *pData){
datum data, key;
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
data.dsize = nData;
data.dptr = pData;
key.dsize = nKey;
key.dptr = pKey;
gdbm_store(pTable->pFile->dbf, key, data, GDBM_REPLACE);
datumClear(&pTable->key);
datumClear(&pTable->data);
return 1;
}
/*
** Remove an entry from a table, if the entry exists.
*/
int sqliteDbbeDelete(DbbeTable *pTable, int nKey, char *pKey){
datum key;
datumClear(&pTable->key);
datumClear(&pTable->data);
if( pTable->pFile==0 || pTable->pFile->dbf==0 ) return 0;
key.dsize = nKey;
key.dptr = pKey;
gdbm_delete(pTable->pFile->dbf, key);
return 1;
}
/*
** Open a temporary file.
*/
FILE *sqliteDbbeOpenTempFile(Dbbe *pBe){
char *zFile;
char zBuf[30];
int i;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==0 ) break;
}
if( i>=pBe->nTemp ){
pBe->nTemp++;
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
}
if( pBe->apTemp==0 ) return 0;
sprintf(zBuf, "/_temp_%d~", i);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
pBe->apTemp[i] = fopen(zFile, "w+");
sqliteFree(zFile);
return pBe->apTemp[i];
}
/*
** Close a temporary file opened using sqliteDbbeOpenTempFile()
*/
void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
int i;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==f ){
char *zFile;
char zBuf[30];
sprintf(zBuf, "/_temp_%d~", i);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
unlink(zFile);
sqliteFree(zFile);
pBe->apTemp[i] = 0;
break;
}
}
fclose(f);
}

119
src/dbbe.h Normal file
View File

@ -0,0 +1,119 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file defines the interface to the database backend (Dbbe).
**
** The database backend is designed to be as general as possible
** so that it can easily be replaced by a different backend.
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
** $Id: dbbe.h,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
#include <stdio.h>
/*
** The database backend supports two opaque structures. A Dbbe is
** a context for the entire set of tables forming a complete
** database. A DbbeTable is a single table.
**
** The DbbeTable structure holds some state information, such as
** the key and data from the last retrieval. For this reason,
** the backend must allow the creation of multiple independent
** DbbeTable structures for each table in the database.
*/
typedef struct Dbbe Dbbe;
typedef struct DbbeTable DbbeTable;
/*
** The 18 interface routines.
*/
/* Open a complete database */
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
/* Close the whole database. */
void sqliteDbbeClose(Dbbe*);
/* Open a particular table of a previously opened database.
** Create the table if it doesn't already exist and writeable!=0.
*/
DbbeTable *sqliteDbbeOpenTable(Dbbe*, const char *zTableName, int writeable);
/* Delete a table from the database */
void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
/* Close a table */
void sqliteDbbeCloseTable(DbbeTable*);
/* Fetch an entry from a table with the given key. Return 1 if
** successful and 0 if no such entry exists.
*/
int sqliteDbbeFetch(DbbeTable*, int nKey, char *pKey);
/* Retrieve the key or data used for the last fetch. Only size
** bytes are read beginning with the offset-th byte. The return
** value is the actual number of bytes read.
*/
int sqliteDbbeCopyKey(DbbeTable*, int offset, int size, char *zBuf);
int sqliteDbbeCopyData(DbbeTable*, int offset, int size, char *zBuf);
/* Retrieve the key or data. The result is ephemeral.
*/
char *sqliteDbbeReadKey(DbbeTable*, int offset);
char *sqliteDbbeReadData(DbbeTable*, int offset);
/* Return the length of the most recently fetched key or data. */
int sqliteDbbeKeyLength(DbbeTable*);
int sqliteDbbeDataLength(DbbeTable*);
/* Retrieve the next entry in the table. The first key is retrieved
** the first time this routine is called, or after a call to
** sqliteDbbeRewind(). The return value is 1 if there is another
** entry, or 0 if there are no more entries. */
int sqliteDbbeNextKey(DbbeTable*);
/* Make it so that the next call to sqliteDbbeNextKey() returns
** the first entry of the table. */
int sqliteDbbeRewind(DbbeTable*);
/* Get a new integer key for this table. */
int sqliteDbbeNew(DbbeTable*);
/* Write an entry into a table. If another entry already exists with
** the same key, the old entry is discarded first.
*/
int sqliteDbbePut(DbbeTable*, int nKey, char *pKey, int nData, char *pData);
/* Remove an entry from the table */
int sqliteDbbeDelete(DbbeTable*, int nKey, char *pKey);
/* Open a file suitable for temporary storage */
FILE *sqliteDbbeOpenTempFile(Dbbe*);
/* Close a temporary file */
void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
#endif /* defined(_SQLITE_DBBE_H_) */

219
src/main.c Normal file
View File

@ -0,0 +1,219 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** Main file for the SQLite library. The routines in this file
** implement the programmer interface to the library. Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#include "sqliteInt.h"
/*
** This is the callback routine for the code that initializes the
** database. Each callback contains text of a CREATE TABLE or
** CREATE INDEX statement that must be parsed to yield the internal
** structures that describe the tables.
*/
static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){
sqlite *db = (sqlite*)pDb;
Parse sParse;
int nErr;
char *zErrMsg = 0;
if( argc!=1 ) return 0;
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.initFlag = 1;
nErr = sqliteRunParser(&sParse, argv[0], &zErrMsg);
return nErr;
}
/*
** Open a new SQLite database. Construct an "sqlite" structure to define
** the state of this database and return a pointer to that structure.
*/
sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
sqlite *db;
Vdbe *vdbe;
Table *pTab;
char *azArg[2];
static char master_schema[] =
"CREATE TABLE " MASTER_NAME " (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" sql text\n"
")"
;
/* The following program is used to initialize the internal
** structure holding the tables and indexes of the database.
** The database contains a special table named "sqlite_master"
** defined as follows:
**
** CREATE TABLE sqlite_master (
** type text, -- Either "table" or "index"
** name text, -- Name of table or index
** tbl_name text, -- Associated table
** sql text -- The CREATE statement for this object
** );
**
** The sqlite_master table contains a single entry for each table
** and each index. The "type" field tells whether the entry is
** a table or index. The "name" field is the name of the object.
** The "tbl_name" is the name of the associated table. For tables,
** the tbl_name field is always the same as name. For indices, the
** tbl_name field contains the name of the table that the index
** indexes. Finally, the sql field contains the complete text of
** the CREATE TABLE or CREATE INDEX statement that originally created
** the table or index.
**
** The following program invokes its callback on the SQL for each
** table then goes back and invokes the callback on the
** SQL for each index. The callback will invoke the
** parser to build the internal representation of the
** database scheme.
*/
static VdbeOp initProg[] = {
{ OP_Open, 0, 0, MASTER_NAME},
{ OP_Next, 0, 8, 0}, /* 1 */
{ OP_Field, 0, 0, 0},
{ OP_String, 0, 0, "table"},
{ OP_Ne, 0, 1, 0},
{ OP_Field, 0, 3, 0},
{ OP_Callback, 1, 0, 0},
{ OP_Goto, 0, 1, 0},
{ OP_Rewind, 0, 0, 0}, /* 8 */
{ OP_Next, 0, 16, 0}, /* 9 */
{ OP_Field, 0, 0, 0},
{ OP_String, 0, 0, "index"},
{ OP_Ne, 0, 9, 0},
{ OP_Field, 0, 3, 0},
{ OP_Callback, 1, 0, 0},
{ OP_Goto, 0, 9, 0},
{ OP_Halt, 0, 0, 0}, /* 16 */
};
/* Allocate space to hold the main database structure */
db = sqliteMalloc( sizeof(sqlite) );
if( pzErrMsg ) *pzErrMsg = 0;
if( db==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
/* Open the backend database driver */
db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg);
if( db->pBe==0 ){
sqliteFree(db);
return 0;
}
/* Create a virtual machine to run the initialization program. Run
** the program. The delete the virtual machine.
*/
azArg[0] = master_schema;
azArg[1] = 0;
sqliteOpenCb(db, 1, azArg, 0);
pTab = sqliteFindTable(db, MASTER_NAME);
if( pTab ){
pTab->readOnly = 1;
}
vdbe = sqliteVdbeCreate(db->pBe);
sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
sqliteVdbeDelete(vdbe);
return db;
}
/*
** Close an existing SQLite database
*/
void sqlite_close(sqlite *db){
int i;
sqliteDbbeClose(db->pBe);
for(i=0; i<N_HASH; i++){
Table *pNext, *pList = db->apTblHash[i];
db->apTblHash[i] = 0;
while( pList ){
pNext = pList->pHash;
pList->pHash = 0;
sqliteDeleteTable(db, pList);
pList = pNext;
}
}
sqliteFree(db);
}
/*
** Return TRUE if the given SQL string ends in a semicolon.
*/
int sqlite_complete(const char *zSql){
int i;
int lastWasSemi = 0;
i = 0;
while( i>=0 && zSql[i]!=0 ){
int tokenType;
int n;
n = sqliteGetToken(&zSql[i], &tokenType);
switch( tokenType ){
case TK_SPACE:
case TK_COMMENT:
break;
case TK_SEMI:
lastWasSemi = 1;
break;
default:
lastWasSemi = 0;
break;
}
i += n;
}
return lastWasSemi;
}
/*
** Execute SQL code
*/
int sqlite_exec(
sqlite *db, /* The database on which the SQL executes */
char *zSql, /* The SQL to be executed */
sqlite_callback xCallback, /* Invoke this callback routine */
void *pArg, /* First argument to xCallback() */
char **pzErrMsg /* Write error messages here */
){
Parse sParse;
int nErr;
if( pzErrMsg ) *pzErrMsg = 0;
memset(&sParse, 0, sizeof(sParse));
sParse.db = db;
sParse.xCallback = xCallback;
sParse.pArg = pArg;
nErr = sqliteRunParser(&sParse, zSql, pzErrMsg);
return nErr;
}

384
src/shell.c Normal file
View File

@ -0,0 +1,384 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
** $Id: shell.c,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sqlite.h"
#include <unistd.h>
#include <ctype.h>
#if !defined(NO_READLINE)
#include <readline/readline.h>
#include <readline/history.h>
#endif
/*
** An pointer to an instance of this structure is passed from
** the main program to the callback. This is used to communicate
** state and mode information.
*/
struct callback_data {
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
int mode; /* An output mode setting */
int showHeader; /* True to show column names in List or Column mode */
char separator[20];/* Separator character for MODE_List */
int colWidth[30]; /* Width of each column when in column mode */
};
/*
** These are the allowed modes.
*/
#define MODE_Line 0 /* One field per line. Blank line between records */
#define MODE_Column 1 /* One record per line in neat columns */
#define MODE_List 2 /* One record per line with a separator */
/*
** Number of elements in an array
*/
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
/*
** This is the callback routine that the SQLite library
** invokes for each row of a query result.
*/
static int callback(void *pArg, int nArg, char **azArg, char **azCol){
int i;
struct callback_data *p = (struct callback_data*)pArg;
switch( p->mode ){
case MODE_Line: {
if( p->cnt++>0 ) fprintf(p->out,"\n");
for(i=0; i<nArg; i++){
fprintf(p->out,"%s = %s\n", azCol[i], azArg[i]);
}
break;
}
case MODE_Column: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
int w;
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
w = p->colWidth[i];
}else{
w = 10;
}
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
}
for(i=0; i<nArg; i++){
int w;
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
w = p->colWidth[i];
}else{
w = 10;
}
fprintf(p->out,"%-*.*s%s",w,w,"-------------------------------------",
i==nArg-1 ? "\n": " ");
}
}
for(i=0; i<nArg; i++){
int w;
if( i<ArraySize(p->colWidth) && p->colWidth[i]>0 ){
w = p->colWidth[i];
}else{
w = 10;
}
fprintf(p->out,"%-*.*s%s",w,w,azArg[i], i==nArg-1 ? "\n": " ");
}
break;
}
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
}
}
for(i=0; i<nArg; i++){
fprintf(p->out,"%s%s",azArg[i], i==nArg-1 ? "\n" : p->separator);
}
break;
}
}
return 0;
}
/*
** Text of a help message
*/
static char zHelp[] =
".exit Exit this program\n"
".explain Set output mode suitable for EXPLAIN\n"
".header ON|OFF Turn display of headers on or off\n"
".help Show this message\n"
".indices TABLE Show names of all indices on TABLE\n"
".mode MODE Set mode to one of \"line\", \"column\", or"
" \"list\"\n"
".output FILENAME Send output to FILENAME\n"
".output stdout Send output to the screen\n"
".schema ?TABLE? Show the CREATE statements\n"
".separator STRING Change separator string for \"list\" mode\n"
".tables List names all tables in the database\n"
".width NUM NUM ... Set column widths for \"column\" mode\n"
;
/*
** If an input line begins with "." then invoke this routine to
** process that line.
*/
static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
int i = 1;
int nArg = 0;
int n, c;
char *azArg[50];
/* Parse the input line into tokens.
*/
while( zLine[i] && nArg<ArraySize(azArg) ){
while( isspace(zLine[i]) ){ i++; }
if( zLine[i]=='\'' || zLine[i]=='"' ){
int delim = zLine[i++];
azArg[nArg++] = &zLine[i];
while( zLine[i] && zLine[i]!=delim ){ i++; }
if( zLine[i]==delim ){
zLine[i++] = 0;
}
}else{
azArg[nArg++] = &zLine[i];
while( zLine[i] && !isspace(zLine[i]) ){ i++; }
if( zLine[i] ) zLine[i++] = 0;
}
}
/* Process the input line.
*/
if( nArg==0 ) return;
n = strlen(azArg[0]);
c = azArg[0][0];
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
exit(0);
}else
if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
p->mode = MODE_Column;
p->showHeader = 1;
p->colWidth[0] = 4;
p->colWidth[1] = 12;
p->colWidth[2] = 5;
p->colWidth[3] = 5;
p->colWidth[4] = 40;
}else
if( c=='h' && strncmp(azArg[0], "header", n)==0 && nArg>1 ){
int j;
char *z = azArg[1];
int val = atoi(azArg[1]);
for(j=0; z[j]; j++){
if( isupper(z[j]) ) z[j] = tolower(z[j]);
}
if( strcmp(z,"on")==0 ){
val = 1;
}else if( strcmp(z,"yes")==0 ){
val = 1;
}
p->showHeader = val;
}else
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
fprintf(stderr,zHelp);
}else
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
struct callback_data data;
char *zErrMsg = 0;
char zSql[1000];
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
sprintf(zSql, "SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name='%.900s'", azArg[1]);
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
}
}else
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
int n2 = strlen(azArg[1]);
if( strncmp(azArg[1],"line",n2)==0 ){
p->mode = MODE_Line;
}else if( strncmp(azArg[1],"column",n2)==0 ){
p->mode = MODE_Column;
}else if( strncmp(azArg[1],"list",n2)==0 ){
p->mode = MODE_List;
}
}else
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
if( p->out!=stdout ){
fclose(p->out);
}
if( strcmp(azArg[1],"stdout")==0 ){
p->out = stdout;
}else{
p->out = fopen(azArg[1], "w");
if( p->out==0 ){
fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
p->out = stdout;
}
}
}else
if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
char zSql[1000];
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
if( nArg>1 ){
sprintf(zSql, "SELECT sql FROM sqlite_master WHERE name='%.900s'",
azArg[1]);
}else{
sprintf(zSql, "SELECT sql FROM sqlite_master "
"ORDER BY tbl_name, type DESC, name");
}
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
}
}else
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
}else
if( c=='t' && strncmp(azArg[0], "tables", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
static char zSql[] = "SELECT name FROM sqlite_master WHERE type='table'";
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
sqlite_exec(db, zSql, callback, &data, &zErrMsg);
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
free(zErrMsg);
}
}else
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
p->colWidth[j-1] = atoi(azArg[j]);
}
}else
{
fprintf(stderr, "unknown command: \"%s\". Enter \".help\" for help\n",
azArg[0]);
}
}
int main(int argc, char **argv){
sqlite *db;
char *zErrMsg = 0;
struct callback_data data;
if( argc!=2 && argc!=3 ){
fprintf(stderr,"Usage: %s FILENAME ?SQL?\n", *argv);
exit(1);
}
db = sqlite_open(argv[1], 0666, &zErrMsg);
if( db==0 ){
fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1], zErrMsg);
exit(1);
}
memset(&data, 0, sizeof(data));
data.out = stdout;
if( argc==3 ){
data.mode = MODE_List;
strcpy(data.separator,"|");
if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){
fprintf(stderr,"SQL error: %s\n", zErrMsg);
exit(1);
}
}else{
char *zLine;
char *zSql = 0;
int nSql = 0;
int istty = isatty(0);
data.mode = MODE_Line;
strcpy(data.separator,"|");
data.showHeader = 0;
if( istty ){
printf(
"Enter \".help\" for instructions\n"
);
}
while( (zLine = readline(istty ? (zSql==0 ? "sql> " : ".... ") : 0))!=0 ){
if( zLine && zLine[0]=='.' ){
do_meta_command(zLine, db, &data);
free(zLine);
continue;
}
if( zSql==0 ){
nSql = strlen(zLine);
zSql = malloc( nSql+1 );
strcpy(zSql, zLine);
}else{
int len = strlen(zLine);
zSql = realloc( zSql, nSql + len + 2 );
if( zSql==0 ){
fprintf(stderr,"%s: out of memory!\n", *argv);
exit(1);
}
strcpy(&zSql[nSql++], "\n");
strcpy(&zSql[nSql], zLine);
nSql += len;
}
free(zLine);
if( sqlite_complete(zSql) ){
data.cnt = 0;
if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0
&& zErrMsg!=0 ){
printf("SQL error: %s\n", zErrMsg);
free(zErrMsg);
zErrMsg = 0;
}
free(zSql);
zSql = 0;
nSql = 0;
}
}
}
sqlite_close(db);
return 0;
}

118
src/sqlite.h Normal file
View File

@ -0,0 +1,118 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This header file defines the interface that the sqlite library
** presents to client programs.
**
** @(#) $Id: sqlite.h,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
/*
** Each open sqlite database is represented by an instance of the
** following opaque structure.
*/
typedef struct sqlite sqlite;
/*
** A function to open a new sqlite database.
**
** If the database does not exist and mode indicates write
** permission, then a new database is created. If the database
** does not exist and mode does not indicate write permission,
** then the open fails, an error message generated (if errmsg!=0)
** and the function returns 0.
**
** If mode does not indicates user write permission, then the
** database is opened read-only.
**
** The Truth: As currently implemented, all databases are opened
** for writing all the time. Maybe someday we will provide the
** ability to open a database readonly. The mode parameters is
** provide in anticipation of that enhancement.
*/
sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
/*
** A function to close the database.
**
** Call this function with a pointer to a structure that was previously
** returned from sqlite_open() and the corresponding database will by closed.
*/
void sqlite_close(sqlite *);
/*
** The type for a callback function.
*/
typedef int (*sqlite_callback)(void*,int,char**, char**);
/*
** A function to executes one or more statements of SQL.
**
** If one or more of the SQL statements are queries, then
** the callback function specified by the 3rd parameter is
** invoked once for each row of the query result. This callback
** should normally return 0. If the callback returns a non-zero
** value then the query is aborted, all subsequent SQL statements
** are skipped and the sqlite_exec() function returns the same
** value that the callback returned.
**
** The 4th parameter is an arbitrary pointer that is passed
** to the callback function as its first parameter.
**
** The 2nd parameter to the callback function is the number of
** columns in the query result. The 3rd parameter is an array
** of string holding the values for each column. The 4th parameter
** is an array of strings holding the names of each column.
**
** The callback function may be NULL, even for queries. A NULL
** callback is not an error. It just means that no callback
** will be invoked.
**
** If an error occurs while parsing or evaluating the SQL (but
** not while executing the callback) then an appropriate error
** message is written into memory obtained from malloc() and
** *errmsg is made to point to that message. If errmsg==NULL,
** then no error message is ever written. The return value is
** non-zero if an error occurs.
*/
int sqlite_exec(
sqlite*, /* An open database */
char *sql, /* SQL to be executed */
sqlite_callback, /* Callback function */
void *, /* 1st argument to callback function */
char **errmsg /* Error msg written here */
);
/* This function returns true if the given input string comprises
** one or more complete SQL statements.
**
** The algorithm is simple. If the last token other than spaces
** and comments is a semicolon, then return true. otherwise return
** false.
*/
int sqlite_complete(const char *sql);
#endif /* _SQLITE_H_ */

235
src/sqliteInt.h Normal file
View File

@ -0,0 +1,235 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
#include "vdbe.h"
#include "parse.h"
#include <gdbm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/*
** The number of entries in the in-memory hash table holding the
** schema.
*/
#define N_HASH 51
/*
** Name of the master database table. The master database table
** is a special table that holds the names and attributes of all
** user tables and indices.
*/
#define MASTER_NAME "sqlite_master"
/*
** A convenience macro that returns the number of elements in
** an array.
*/
#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
/*
** Forward references to structures
*/
typedef struct Table Table;
typedef struct Index Index;
typedef struct Instruction Instruction;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct Parse Parse;
typedef struct Token Token;
typedef struct IdList IdList;
typedef struct WhereInfo WhereInfo;
/*
** Each database is an instance of the following structure
*/
struct sqlite {
Dbbe *pBe; /* The backend driver */
int flags; /* Miscellanous flags */
Table *apTblHash[N_HASH]; /* All tables of the database */
Index *apIdxHash[N_HASH]; /* All indices of the database */
};
/*
** Possible values for the flags field of sqlite
*/
#define SQLITE_VdbeTrace 0x00000001
/*
** Each table is represented in memory by
** an instance of the following structure
*/
struct Table {
char *zName; /* Name of the table */
Table *pHash; /* Next table with same hash on zName */
int nCol; /* Number of columns in this table */
int readOnly; /* True if this table should not be written by the user */
char **azCol; /* Name of each column */
Index *pIndex; /* List of indices on this table. */
};
/*
** Each index is represented in memory by and
** instance of the following structure.
*/
struct Index {
char *zName; /* Name of this index */
Index *pHash; /* Next index with the same hash on zName */
int nField; /* Number of fields in the table indexed by this index */
int *aiField; /* Indices of fields used by this index. 1st is 0 */
Table *pTable; /* The table being indexed */
Index *pNext; /* The next index associated with the same table */
};
/*
** Each token coming out of the lexer is an instance of
** this structure.
*/
struct Token {
char *z; /* Text of the token */
int n; /* Number of characters in this token */
};
/*
** Each node of an expression in the parse tree is an instance
** of this structure
*/
struct Expr {
int op; /* Operation performed by this node */
Expr *pLeft, *pRight; /* Left and right subnodes */
ExprList *pList; /* A list of expressions used as a function argument */
Token token; /* An operand token */
int iTable, iField; /* When op==TK_FIELD, then this node means the
** iField-th field of the iTable-th table */
};
/*
** A list of expressions. Each expression may optionally have a
** name. An expr/name combination can be used in several ways, such
** as the list of "expr AS ID" fields following a "SELECT" or in the
** list of "ID = expr" items in an UPDATE. A list of expressions can
** also be used as the argument to a function, in which case the azName
** field is not used.
*/
struct ExprList {
int nExpr; /* Number of expressions on the list */
struct {
Expr *pExpr; /* The list of expressions */
char *zName; /* Token associated with this expression */
int idx; /* ... */
} *a; /* One entry for each expression */
};
/*
** A list of identifiers.
*/
struct IdList {
int nId; /* Number of identifiers on the list */
struct {
char *zName; /* Text of the identifier. */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
Table *pTab; /* Table corresponding to zName */
int idx; /* Index of a field name in the table */
} *a; /* One entry for each identifier on the list */
};
/*
** The WHERE clause processing routine has two halves. The
** first part does the start of the WHERE loop and the second
** half does the tail of the WHERE loop. An instance of
** this structure is returned by the first half and passed
** into the second half to give some continuity.
*/
struct WhereInfo {
Parse *pParse;
IdList *pTabList;
int iContinue;
int iBreak;
};
/*
** An SQL parser context
*/
struct Parse {
sqlite *db; /* The main database structure */
sqlite_callback xCallback; /* The callback function */
void *pArg; /* First argument to the callback function */
char *zErrMsg; /* An error message */
Token sErrToken; /* The token at which the error occurred */
Token sFirstToken; /* The first token parsed */
Token sLastToken; /* The last token parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Vdbe *pVdbe; /* An engine for executing database bytecode */
int explain; /* True if the EXPLAIN flag is found on the query */
int initFlag; /* True if reparsing CREATE TABLEs */
int nErr; /* Number of errors seen */
};
/*
** Internal function prototypes
*/
int sqliteStrICmp(const char *, const char *);
int sqliteStrNICmp(const char *, const char *, int);
int sqliteHashNoCase(const char *, int);
int sqliteCompare(const char *, const char *);
int sqliteSortCompare(const char *, const char *);
void *sqliteMalloc(int);
void sqliteFree(void*);
void *sqliteRealloc(void*,int);
int sqliteGetToken(const char*, int *);
void sqliteSetString(char **, const char *, ...);
void sqliteSetNString(char **, ...);
int sqliteRunParser(Parse*, char*, char **);
void sqliteExec(Parse*);
Expr *sqliteExpr(int, Expr*, Expr*, Token*);
Expr *sqliteExprFunction(ExprList*, Token*);
void sqliteExprDelete(Expr*);
ExprList *sqliteExprListAppend(ExprList*,Expr*,Token*);
void sqliteExprListDelete(ExprList*);
void sqliteStartTable(Parse*,Token*,Token*);
void sqliteAddColumn(Parse*,Token*);
void sqliteEndTable(Parse*,Token*);
void sqliteDropTable(Parse*, Token*);
void sqliteDeleteTable(sqlite*, Table*);
void sqliteInsert(Parse*, Token*, ExprList*, IdList*);
IdList *sqliteIdListAppend(IdList*, Token*);
void sqliteIdListAddAlias(IdList*, Token*);
void sqliteIdListDelete(IdList*);
void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, Token*, Token*);
void sqliteDropIndex(Parse*, Token*);
void sqliteSelect(Parse*, ExprList*, IdList*, Expr*, ExprList*);
void sqliteDeleteFrom(Parse*, Token*, Expr*);
void sqliteUpdate(Parse*, Token*, ExprList*, Expr*);
WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
void sqliteWhereEnd(WhereInfo*);
void sqliteExprCode(Parse*, Expr*);
void sqliteExprIfTrue(Parse*, Expr*, int);
void sqliteExprIfFalse(Parse*, Expr*, int);
Table *sqliteFindTable(sqlite*,char*);

241
src/tclsqlite.c Normal file
View File

@ -0,0 +1,241 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.1 2000/05/29 14:26:01 drh Exp $
*/
#include "sqlite.h"
#include <tcl.h>
#include <stdlib.h>
#include <string.h>
/*
** An instance of this structure passes information thru the sqlite
** logic from the original TCL command into the callback routine.
*/
typedef struct CallbackData CallbackData;
struct CallbackData {
Tcl_Interp *interp; /* The TCL interpreter */
char *zArray; /* The array into which data is written */
char *zCode; /* The code to execute for each row */
int once; /* Set only for the first invocation of callback */
};
/*
** Called for each row of the result.
*/
static int DbEvalCallback(
void *clientData, /* An instance of CallbackData */
int nCol, /* Number of columns in the result */
char ** azCol, /* Data for each column */
char ** azN /* Name for each column */
){
CallbackData *cbData = (CallbackData*)clientData;
int i, rc;
if( cbData->zArray[0] ){
if( cbData->once ){
for(i=0; i<nCol; i++){
Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
}
}
for(i=0; i<nCol; i++){
Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], azCol[i], 0);
}
}else{
for(i=0; i<nCol; i++){
Tcl_SetVar(cbData->interp, azN[i], azCol[i], 0);
}
}
cbData->once = 0;
rc = Tcl_Eval(cbData->interp, cbData->zCode);
return rc;
}
/*
** Called when the command is deleted.
*/
static void DbDeleteCmd(void *db){
sqlite_close((sqlite*)db);
}
/*
** The "sqlite" command below creates a new Tcl command for each
** connection it opens to an SQLite database. This routine is invoked
** whenever one of those connection-specific commands is executed
** in Tcl. For example, if you run Tcl code like this:
**
** sqlite db1 "my_database"
** db1 close
**
** The first command opens a connection to the "my_database" database
** and calls that connection "db1". The second command causes this
** subroutine to be invoked.
*/
static int DbCmd(void *cd, Tcl_Interp *interp, int argc, char **argv){
char *z;
int n, c;
sqlite *db = cd;
if( argc<2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" SUBCOMMAND ...\"", 0);
return TCL_ERROR;
}
z = argv[1];
n = strlen(z);
c = z[0];
/* $db close
**
** Shutdown the database
*/
if( c=='c' && n>=2 && strncmp(z,"close",n)==0 ){
Tcl_DeleteCommand(interp, argv[0]);
}else
/* $db complete SQL
**
** Return TRUE if SQL is a complete SQL statement. Return FALSE if
** additional lines of input are needed. This is similar to the
** built-in "info complete" command of Tcl.
*/
if( c=='c' && n>=2 && strncmp(z,"complete",n)==0 ){
char *zRes;
if( argc!=3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" complete SQL\"", 0);
return TCL_ERROR;
}
zRes = sqlite_complete(argv[2]) ? "1" : "0";
Tcl_SetResult(interp, zRes, TCL_VOLATILE);
}else
/*
** $db eval $sql ?array { ...code... }?
**
** The SQL statement in $sql is evaluated. For each row, the values are
** placed in elements of the array named "array" and ...code.. is executed.
** If "array" and "code" are omitted, then no callback is every invoked.
** If "array" is an empty string, then the values are placed in variables
** that have the same name as the fields extracted by the query.
*/
if( c=='e' && strncmp(z,"eval",n)==0 ){
CallbackData cbData;
char *zErrMsg;
int rc;
if( argc!=5 && argc!=3 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" eval SQL ?ARRAY-NAME CODE?", 0);
return TCL_ERROR;
}
if( argc==5 ){
cbData.interp = interp;
cbData.zArray = argv[3];
cbData.zCode = argv[4];
zErrMsg = 0;
rc = sqlite_exec(db, argv[2], DbEvalCallback, &cbData, &zErrMsg);
}else{
rc = sqlite_exec(db, argv[2], 0, 0, &zErrMsg);
}
if( zErrMsg ){
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
free(zErrMsg);
}
return rc;
}
/* The default
*/
else{
Tcl_AppendResult(interp,"unknown subcommand \"", z,
"\" - should be one of: close complete eval", 0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** sqlite DBNAME FILENAME ?MODE?
**
** This is the main Tcl command. When the "sqlite" Tcl command is
** invoked, this routine runs to process that command.
**
** The first argument, DBNAME, is an arbitrary name for a new
** database connection. This command creates a new command named
** DBNAME that is used to control that connection. The database
** connection is deleted when the DBNAME command is deleted.
**
** The second argument is the name of the directory that contains
** the sqlite database that is to be accessed.
*/
static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){
int mode;
sqlite *p;
char *zErrMsg;
if( argc!=3 && argc!=4 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
" HANDLE FILENAME ?MODE?\"", 0);
return TCL_ERROR;
}
if( argc==3 ){
mode = 0;
}else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){
return TCL_ERROR;
}
zErrMsg = 0;
p = sqlite_open(argv[2], mode, &zErrMsg);
if( p==0 ){
Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE);
free(zErrMsg);
return TCL_ERROR;
}
Tcl_CreateCommand(interp, argv[1], DbCmd, p, DbDeleteCmd);
return TCL_OK;
}
/*
** Initialize this module.
**
** This Tcl module contains only a single new Tcl command named "sqlite".
** (Hence there is no namespace. There is no point in using a namespace
** if the extension only supplies one new name!) The "sqlite" command is
** used to open a new SQLite database. See the DbMain() routine above
** for additional information.
*/
int Sqlite_Init(Tcl_Interp *interp){
Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0);
return TCL_OK;
}
int Sqlite_SafeInit(Tcl_Interp *interp){
return TCL_OK;
}
/*
** If compiled using mktclapp, this routine runs to initialize
** everything.
*/
int Et_AppInit(Tcl_Interp *interp){
return Sqlite_Init(interp);
}

375
src/tokenize.c Normal file
View File

@ -0,0 +1,375 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** An tokenizer for SQL
**
** This file contains C code that splits an SQL input string up into
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
** $Id: tokenize.c,v 1.1 2000/05/29 14:26:02 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
/*
** All the keywords of the SQL language are stored as in a hash
** table composed of instances of the following structure.
*/
typedef struct Keyword Keyword;
struct Keyword {
char *zName; /* The keyword name */
int len; /* Number of characters in the keyword */
int tokenType; /* The token value for this keyword */
Keyword *pNext; /* Next keyword with the same hash */
};
/*
** These are the keywords
*/
static Keyword aKeywordTable[] = {
{ "AND", 0, TK_AND, 0 },
{ "AS", 0, TK_AS, 0 },
{ "ASC", 0, TK_ASC, 0 },
{ "BY", 0, TK_BY, 0 },
{ "CHECK", 0, TK_CHECK, 0 },
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
{ "CREATE", 0, TK_CREATE, 0 },
{ "DEFAULT", 0, TK_DEFAULT, 0 },
{ "DELETE", 0, TK_DELETE, 0 },
{ "DESC", 0, TK_DESC, 0 },
{ "DROP", 0, TK_DROP, 0 },
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
{ "FROM", 0, TK_FROM, 0 },
{ "INDEX", 0, TK_INDEX, 0 },
{ "INSERT", 0, TK_INSERT, 0 },
{ "INTO", 0, TK_INTO, 0 },
{ "IS", 0, TK_IS, 0 },
{ "ISNULL", 0, TK_ISNULL, 0 },
{ "KEY", 0, TK_KEY, 0 },
{ "NOT", 0, TK_NOT, 0 },
{ "NOTNULL", 0, TK_NOTNULL, 0 },
{ "NULL", 0, TK_NULL, 0 },
{ "ON", 0, TK_ON, 0 },
{ "OR", 0, TK_OR, 0 },
{ "ORDER", 0, TK_ORDER, 0 },
{ "PRIMARY", 0, TK_PRIMARY, 0 },
{ "SELECT", 0, TK_SELECT, 0 },
{ "SET", 0, TK_SET, 0 },
{ "TABLE", 0, TK_TABLE, 0 },
{ "UNIQUE", 0, TK_UNIQUE, 0 },
{ "UPDATE", 0, TK_UPDATE, 0 },
{ "VALUES", 0, TK_VALUES, 0 },
{ "WHERE", 0, TK_WHERE, 0 },
};
/*
** This is the hash table
*/
#define KEY_HASH_SIZE 37
static Keyword *apHashTable[KEY_HASH_SIZE];
/*
** This function looks up an identifier to determine if it is a
** keyword. If it is a keyword, the token code of that keyword is
** returned. If the input is not a keyword, TK_ID is returned.
*/
static int sqliteKeywordCode(const char *z, int n){
int h;
Keyword *p;
if( aKeywordTable[0].len==0 ){
/* Initialize the keyword hash table */
int i;
int n;
n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
for(i=0; i<n; i++){
aKeywordTable[i].len = strlen(aKeywordTable[i].zName);
h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len);
h %= KEY_HASH_SIZE;
aKeywordTable[i].pNext = apHashTable[h];
apHashTable[h] = &aKeywordTable[i];
}
}
h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE;
for(p=apHashTable[h]; p; p=p->pNext){
if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
return p->tokenType;
}
}
return TK_ID;
}
/*
** Return the length of the token that begins at z[0]. Return
** -1 if the token is (or might be) incomplete. Store the token
** type in *tokenType before returning.
*/
int sqliteGetToken(const char *z, int *tokenType){
int i;
switch( *z ){
case ' ': case '\t': case '\n': case '\f': {
for(i=1; z[i] && isspace(z[i]); i++){}
*tokenType = TK_SPACE;
return i;
}
case '-': {
if( z[1]==0 ) return -1;
if( z[1]=='-' ){
for(i=2; z[i] && z[i]!='\n'; i++){}
*tokenType = TK_COMMENT;
return i;
}
*tokenType = TK_MINUS;
return 1;
}
case '(': {
*tokenType = TK_LP;
return 1;
}
case ')': {
*tokenType = TK_RP;
return 1;
}
case ';': {
*tokenType = TK_SEMI;
return 1;
}
case '+': {
*tokenType = TK_PLUS;
return 1;
}
case '*': {
*tokenType = TK_STAR;
return 1;
}
case '/': {
*tokenType = TK_SLASH;
return 1;
}
case '=': {
*tokenType = TK_EQ;
return 1 + (z[1]=='=');
}
case '<': {
if( z[1]=='=' ){
*tokenType = TK_LE;
return 2;
}else if( z[1]=='>' ){
*tokenType = TK_NE;
return 2;
}else{
*tokenType = TK_LT;
return 1;
}
}
case '>': {
if( z[1]=='=' ){
*tokenType = TK_GE;
return 2;
}else{
*tokenType = TK_GT;
return 1;
}
}
case '!': {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
return 1;
}else{
*tokenType = TK_NE;
return 2;
}
}
case ',': {
*tokenType = TK_COMMA;
return 1;
}
case '\'': case '"': {
int delim = z[0];
for(i=1; z[i]; i++){
if( z[i]==delim ){
if( z[i+1]==delim ){
i++;
}else{
break;
}
}
}
if( z[i] ) i++;
*tokenType = TK_STRING;
return i;
}
case '.': {
if( !isdigit(z[1]) ){
*tokenType = TK_DOT;
return 1;
}
/* 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': {
for(i=1; z[i] && isdigit(z[i]); i++){}
if( z[i]=='.' ){
i++;
while( z[i] && isdigit(z[i]) ){ i++; }
if( (z[i]=='e' || z[i]=='E') &&
( isdigit(z[i+1])
|| ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
)
){
i += 2;
while( z[i] && isdigit(z[i]) ){ i++; }
}
*tokenType = TK_FLOAT;
}else if( z[0]=='.' ){
*tokenType = TK_FLOAT;
}else{
*tokenType = TK_INTEGER;
}
return i;
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z': case '_':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z': {
for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){}
*tokenType = sqliteKeywordCode(z, i);
return i;
}
default: {
break;
}
}
*tokenType = TK_ILLEGAL;
return 1;
}
/*
** Run the parser on the given SQL string. The parser structure is
** passed in. Return the number of errors.
*/
int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
int nErr = 0;
int i;
void *pEngine;
int once = 1;
static FILE *trace = 0;
extern void *sqliteParserAlloc(void*(*)(int));
extern void sqliteParserFree(void*, void(*)(void*));
extern int sqliteParser(void*, int, ...);
extern void sqliteParserTrace(FILE*, char *);
i = 0;
pEngine = sqliteParserAlloc(sqliteMalloc);
if( pEngine==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 1;
}
sqliteParserTrace(trace, "parser: ");
while( nErr==0 && i>=0 && zSql[i]!=0 ){
int tokenType;
pParse->sLastToken.z = &zSql[i];
pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType);
i += pParse->sLastToken.n;
if( once ){
pParse->sFirstToken = pParse->sLastToken;
once = 0;
}
switch( tokenType ){
case TK_SPACE:
break;
case TK_COMMENT: {
/* Various debugging modes can be turned on and off using
** special SQL comments. Check for the special comments
** here and take approriate action if found.
*/
char *z = pParse->sLastToken.z;
if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){
trace = stderr;
sqliteParserTrace(trace, "parser: ");
}else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){
trace = 0;
sqliteParserTrace(trace, "parser: ");
}else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){
pParse->db->flags |= SQLITE_VdbeTrace;
}else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 19)==0 ){
pParse->db->flags &= ~SQLITE_VdbeTrace;
}
break;
}
case TK_ILLEGAL:
sqliteSetNString(pzErrMsg, "illegal token: \"", -1,
pParse->sLastToken.z, pParse->sLastToken.n, 0);
nErr++;
break;
default:
sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
if( pParse->zErrMsg ){
sqliteSetNString(pzErrMsg, "near \"", -1,
pParse->sErrToken.z, pParse->sErrToken.n,
"\": ", -1,
pParse->zErrMsg, -1,
0);
nErr++;
}
break;
}
}
if( nErr==0 ){
sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
if( pParse->zErrMsg ){
sqliteSetNString(pzErrMsg, "near \"", -1,
pParse->sErrToken.z, pParse->sErrToken.n,
"\": ", -1,
pParse->zErrMsg, -1,
0);
nErr++;
}
}
sqliteParserFree(pEngine, sqliteFree);
if( pParse->zErrMsg ){
if( pzErrMsg ){
*pzErrMsg = pParse->zErrMsg;
}else{
sqliteFree(pParse->zErrMsg);
}
if( !nErr ) nErr++;
}
if( pParse->pVdbe ){
sqliteVdbeDelete(pParse->pVdbe);
pParse->pVdbe = 0;
}
if( pParse->pNewTable ){
sqliteDeleteTable(pParse->db, pParse->pNewTable);
pParse->pNewTable = 0;
}
return nErr;
}

445
src/util.c Normal file
View File

@ -0,0 +1,445 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.1 2000/05/29 14:26:02 drh Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
#include <ctype.h>
/*
** Allocate new memory and set it to zero. Return NULL if
** no memory is available.
*/
void *sqliteMalloc(int n){
void *p = malloc(n);
if( p==0 ) return 0;
memset(p, 0, n);
return p;
}
/*
** Free memory previously obtained from sqliteMalloc()
*/
void sqliteFree(void *p){
if( p ) free(p);
}
/*
** Resize a prior allocation. If p==0, then this routine
** works just like sqliteMalloc(). If n==0, then this routine
** works just like sqliteFree().
*/
void *sqliteRealloc(void *p, int n){
if( p==0 ){
return sqliteMalloc(n);
}
if( n==0 ){
sqliteFree(p);
return 0;
}
return realloc(p, n);
}
/*
** Create a string from the 2nd and subsequent arguments (up to the
** first NULL argument), store the string in memory obtained from
** sqliteMalloc() and make the pointer indicated by the 1st argument
** point to that string.
*/
void sqliteSetString(char **pz, const char *zFirst, ...){
va_list ap;
int nByte;
const char *z;
char *zResult;
if( pz==0 ) return;
nByte = strlen(zFirst) + 1;
va_start(ap, zFirst);
while( (z = va_arg(ap, const char*))!=0 ){
nByte += strlen(z);
}
va_end(ap);
sqliteFree(*pz);
*pz = zResult = sqliteMalloc( nByte );
if( zResult==0 ) return;
strcpy(zResult, zFirst);
zResult += strlen(zResult);
va_start(ap, zFirst);
while( (z = va_arg(ap, const char*))!=0 ){
strcpy(zResult, z);
zResult += strlen(zResult);
}
va_end(ap);
}
/*
** Works like sqliteSetString, but each string is now followed by
** a length integer. -1 means use the whole string.
*/
void sqliteSetNString(char **pz, ...){
va_list ap;
int nByte;
const char *z;
char *zResult;
int n;
if( pz==0 ) return;
nByte = 0;
va_start(ap, pz);
while( (z = va_arg(ap, const char*))!=0 ){
n = va_arg(ap, int);
if( n<=0 ) n = strlen(z);
nByte += n;
}
va_end(ap);
sqliteFree(*pz);
*pz = zResult = sqliteMalloc( nByte + 1 );
if( zResult==0 ) return;
va_start(ap, pz);
while( (z = va_arg(ap, const char*))!=0 ){
n = va_arg(ap, int);
if( n<=0 ) n = strlen(z);
strncpy(zResult, z, n);
zResult += n;
}
*zResult = 0;
va_end(ap);
}
/* An array to map all upper-case characters into their corresponding
** lower-case character.
*/
static unsigned char UpperToLower[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
252,253,254,255
};
/*
** This function computes a hash on the name of a keyword.
** Case is not significant.
*/
int sqliteHashNoCase(const char *z, int n){
int h = 0;
int c;
if( n<=0 ) n = strlen(z);
while( n-- > 0 && (c = *z++)!=0 ){
h = h<<3 ^ h ^ UpperToLower[c];
}
if( h<0 ) h = -h;
return h;
}
/*
** Some system shave stricmp(). Others have strcasecmp(). Because
** there is no consistency, we will define our own.
*/
int sqliteStrICmp(const char *zLeft, const char *zRight){
register unsigned char *a, *b;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
return *a - *b;
}
int sqliteStrNICmp(const char *zLeft, const char *zRight, int N){
register unsigned char *a, *b;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
return N<=0 ? 0 : *a - *b;
}
/* Notes on string comparisions.
**
** We want the main string comparision function used for sorting to
** sort both numbers and alphanumeric words into the correct sequence.
** The same routine should do both without prior knowledge of which
** type of text the input represents. It should even work for strings
** which are a mixture of text and numbers.
**
** To accomplish this, we keep track of a state number while scanning
** the two strings. The states are as follows:
**
** 1 Beginning of word
** 2 Arbitrary text
** 3 Integer
** 4 Negative integer
** 5 Real number
** 6 Negative real
**
** The scan begins in state 1, beginning of word. Transitions to other
** states are determined by characters seen, as shown in the following
** chart:
**
** Current State Character Seen New State
** -------------------- -------------- -------------------
** 0 Beginning of word "-" 3 Negative integer
** digit 2 Integer
** space 0 Beginning of word
** otherwise 1 Arbitrary text
**
** 1 Arbitrary text space 0 Beginning of word
** digit 2 Integer
** otherwise 1 Arbitrary text
**
** 2 Integer space 0 Beginning of word
** "." 4 Real number
** digit 2 Integer
** otherwise 1 Arbitrary text
**
** 3 Negative integer space 0 Beginning of word
** "." 5 Negative Real num
** digit 3 Negative integer
** otherwise 1 Arbitrary text
**
** 4 Real number space 0 Beginning of word
** digit 4 Real number
** otherwise 1 Arbitrary text
**
** 5 Negative real num space 0 Beginning of word
** digit 5 Negative real num
** otherwise 1 Arbitrary text
**
** To implement this state machine, we first classify each character
** into on of the following categories:
**
** 0 Text
** 1 Space
** 2 Digit
** 3 "-"
** 4 "."
**
** Given an arbitrary character, the array charClass[] maps that character
** into one of the atove categories.
*/
static const unsigned char charClass[] = {
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
/* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
/* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0,
/* 3x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
/* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 9x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Ax */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Bx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Cx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Dx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Ex */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* Fx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
#define N_CHAR_CLASS 5
/*
** Given the current state number (0 thru 5), this array figures
** the new state number given the character class.
*/
static const unsigned char stateMachine[] = {
/* Text, Space, Digit, "-", "." */
1, 0, 2, 3, 1, /* State 0: Beginning of word */
1, 0, 2, 1, 1, /* State 1: Arbitrary text */
1, 0, 2, 1, 4, /* State 2: Integer */
1, 0, 3, 1, 5, /* State 3: Negative integer */
1, 0, 4, 1, 1, /* State 4: Real number */
1, 0, 5, 1, 1, /* State 5: Negative real num */
};
/* This routine does a comparison of two strings. Case is used only
** if useCase!=0. Numbers compare in numerical order.
*/
static int privateStrCmp(const char *atext, const char *btext, int useCase){
register unsigned char *a, *b, *map, ca, cb;
int result;
register int cclass = 0;
a = (unsigned char *)atext;
b = (unsigned char *)btext;
if( useCase ){
do{
if( (ca= *a++)!=(cb= *b++) ) break;
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
}while( ca!=0 );
}else{
map = UpperToLower;
do{
if( (ca=map[*a++])!=(cb=map[*b++]) ) break;
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
}while( ca!=0 );
}
switch( cclass ){
case 0:
case 1: {
if( isdigit(ca) && isdigit(cb) ){
cclass = 2;
}
break;
}
default: {
break;
}
}
switch( cclass ){
case 2:
case 3: {
if( isdigit(ca) ){
if( isdigit(cb) ){
int acnt, bcnt;
acnt = bcnt = 0;
while( isdigit(*a++) ) acnt++;
while( isdigit(*b++) ) bcnt++;
result = acnt - bcnt;
if( result==0 ) result = ca-cb;
}else{
result = 1;
}
}else if( isdigit(cb) ){
result = -1;
}else if( ca=='.' ){
result = 1;
}else if( cb=='.' ){
result = -1;
}else{
result = ca - cb;
cclass = 2;
}
if( cclass==3 ) result = -result;
break;
}
case 0:
case 1:
case 4: {
result = ca - cb;
break;
}
case 5: {
result = cb - ca;
};
}
return result;
}
/* This comparison routine is what we use for comparison operations
** in an SQL expression. (Ex: name<'Hello' or value<5). Compare two
** strings. Use case only as a tie-breaker. Numbers compare in
** numerical order.
*/
int sqliteCompare(const char *atext, const char *btext){
int result;
result = privateStrCmp(atext, btext, 0);
if( result==0 ) result = privateStrCmp(atext, btext, 1);
return result;
}
/*
** If you compile just this one file with the -DTEST_COMPARE=1 option,
** it generates a program to test the comparisons routines.
*/
#ifdef TEST_COMPARE
#include <stdlib.h>
#include <stdio.h>
int sortCmp(const char **a, const char **b){
return sqliteCompare(*a, *b);
}
int main(int argc, char **argv){
int i, j, k, n;
static char *azStr[] = {
"abc", "aBc", "abcd", "aBcd",
"123", "124", "1234", "-123", "-124", "-1234",
"123.45", "123.456", "123.46", "-123.45", "-123.46", "-123.456",
"x9", "x10", "x-9", "x-10", "X9", "X10",
};
n = sizeof(azStr)/sizeof(azStr[0]);
qsort(azStr, n, sizeof(azStr[0]), sortCmp);
for(i=0; i<n; i++){
printf("%s\n", azStr[i]);
}
printf("Sanity1...");
fflush(stdout);
for(i=0; i<n-1; i++){
char *a = azStr[i];
for(j=i+1; j<n; j++){
char *b = azStr[j];
if( sqliteCompare(a,b) != -sqliteCompare(b,a) ){
printf("Failed! \"%s\" vs \"%s\"\n", a, b);
i = j = n;
}
}
}
if( i<n ){
printf(" OK\n");
}
return 0;
}
#endif
/*
** This routine is used for sorting. Each key is a list one or more
** null-terminated strings. The list is terminated by two null in
** a row. For example, the following text is strings:
**
** +one\000-two\000+three\000\000
**
** Both arguments will have the same number of strings. This routine
** returns negative, zero, or positive if the first argument is less
** than, equal to, or greater than the first. (Result is a-b).
**
** Every string begins with either a "+" or "-" character. If the
** character is "-" then the return value is negated. This is done
** to implement a sort in descending order.
*/
int sqliteSortCompare(const char *a, const char *b){
int len;
int res = 0;
while( res==0 && *a && *b ){
res = sqliteCompare(&a[1], &b[1]);
if( res==0 ){
len = strlen(a) + 1;
a += len;
b += len;
}
}
if( *a=='-' ) res = -res;
return res;
}

1973
src/vdbe.c Normal file

File diff suppressed because it is too large Load Diff

166
src/vdbe.h Normal file
View File

@ -0,0 +1,166 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** Header file for the Virtual DataBase Engine (VDBE)
**
** This header defines the interface to the virtual database engine
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
** $Id: vdbe.h,v 1.1 2000/05/29 14:26:02 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
#include <stdio.h>
/*
** A single VDBE is an opaque structure named "Vdbe". Only routines
** in the source file sqliteVdbe.c are allowed to see the insides
** of this structure.
*/
typedef struct Vdbe Vdbe;
/*
** A single instruction of the virtual machine has an opcode
** and as many as three operands. The instruction is recorded
** as an instance of the following structure:
*/
struct VdbeOp {
int opcode; /* What operation to perform */
int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */
char *p3; /* Third parameter */
};
typedef struct VdbeOp VdbeOp;
/*
** The following macro converts a relative address in the p2 field
** of a VdbeOp structure into a negative number so that
** sqliteVdbeAddOpList() knows that the address is relative. Calling
** the macro again restores the address.
*/
#define ADDR(X) (-1-(X))
/*
** These are the available opcodes.
**
** If any of the values changes or if opcodes are added or removed,
** be sure to also update the zOpName[] array in sqliteVdbe.c to
** mirror the change.
**
** The source tree contains an AWK script named renumberOps.awk that
** can be used to renumber these opcodes when new opcodes are inserted.
*/
#define OP_Open 1
#define OP_Close 2
#define OP_Destroy 3
#define OP_Fetch 4
#define OP_New 5
#define OP_Put 6
#define OP_Delete 7
#define OP_Field 8
#define OP_Key 9
#define OP_Rewind 10
#define OP_Next 11
#define OP_ResetIdx 12
#define OP_NextIdx 13
#define OP_PutIdx 14
#define OP_DeleteIdx 15
#define OP_ListOpen 16
#define OP_ListWrite 17
#define OP_ListRewind 18
#define OP_ListRead 19
#define OP_ListClose 20
#define OP_SortOpen 21
#define OP_SortPut 22
#define OP_SortMakeRec 23
#define OP_SortMakeKey 24
#define OP_Sort 25
#define OP_SortNext 26
#define OP_SortKey 27
#define OP_SortCallback 28
#define OP_SortClose 29
#define OP_MakeRecord 30
#define OP_MakeKey 31
#define OP_Goto 32
#define OP_If 33
#define OP_Halt 34
#define OP_ColumnCount 35
#define OP_ColumnName 36
#define OP_Callback 37
#define OP_Integer 38
#define OP_String 39
#define OP_Pop 40
#define OP_Dup 41
#define OP_Pull 42
#define OP_Add 43
#define OP_AddImm 44
#define OP_Subtract 45
#define OP_Multiply 46
#define OP_Divide 47
#define OP_Min 48
#define OP_Max 49
#define OP_Eq 50
#define OP_Ne 51
#define OP_Lt 52
#define OP_Le 53
#define OP_Gt 54
#define OP_Ge 55
#define OP_IsNull 56
#define OP_NotNull 57
#define OP_Negative 58
#define OP_And 59
#define OP_Or 60
#define OP_Not 61
#define OP_Concat 62
#define OP_Noop 63
#define OP_MAX 63
/*
** Prototypes for the VDBE interface. See comments on the implementation
** for a description of what each of these routines does.
*/
Vdbe *sqliteVdbeCreate(Dbbe*);
int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int);
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
void sqliteVdbeDequoteP3(Vdbe*, int addr);
int sqliteVdbeMakeLabel(Vdbe*);
void sqliteVdbeDelete(Vdbe*);
int sqliteVdbeOpcode(const char *zName);
int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**);
int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
void sqliteVdbeResolveLabel(Vdbe*, int);
int sqliteVdbeCurrentAddr(Vdbe*);
void sqliteVdbeTrace(Vdbe*,FILE*);
#endif

566
src/where.c Normal file
View File

@ -0,0 +1,566 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
** the WHERE clause of SQL statements. Also found here are subroutines
** to generate VDBE code to evaluate expressions.
**
** $Id: where.c,v 1.1 2000/05/29 14:26:02 drh Exp $
*/
#include "sqliteInt.h"
/*
** The query generator uses an array of instances of this structure to
** help it analyze the subexpressions of the WHERE clause. Each WHERE
** clause subexpression is separated from the others by an AND operator.
*/
typedef struct ExprInfo ExprInfo;
struct ExprInfo {
Expr *p; /* Pointer to the subexpression */
int indexable; /* True if this subexprssion is usable by an index */
int idxLeft; /* p->pLeft is a field in this table number. -1 if
** p->pLeft is not the field of any table */
int idxRight; /* p->pRight is a field in this table number. -1 if
** p->pRight is not the field of any table */
unsigned prereqLeft; /* Tables referenced by p->pLeft */
unsigned prereqRight; /* Tables referenced by p->pRight */
};
/*
** Determine the number of elements in an array.
*/
#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
/*
** This routine is used to divide the WHERE expression into subexpressions
** separated by the AND operator.
**
** aSlot[] is an array of subexpressions structures.
** There are nSlot spaces left in this array. This routine attempts to
** split pExpr into subexpressions and fills aSlot[] with those subexpressions.
** The return value is the number of slots filled.
*/
static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
int cnt = 0;
if( pExpr==0 || nSlot<1 ) return 0;
if( nSlot==1 || pExpr->op!=TK_AND ){
aSlot[0].p = pExpr;
return 1;
}
if( pExpr->pLeft->op!=TK_AND ){
aSlot[0].p = pExpr->pLeft;
cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight);
}else{
cnt = exprSplit(nSlot, aSlot, pExpr->pRight);
cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pLeft);
}
return cnt;
}
/*
** This routine walks (recursively) an expression tree and generates
** a bitmask indicating which tables are used in that expression
** tree. Bit 0 of the mask is set if table 0 is used. But 1 is set
** if table 1 is used. And so forth.
**
** In order for this routine to work, the calling function must have
** previously invoked sqliteExprResolveIds() on the expression. See
** the header comment on that routine for additional information.
*/
static int exprTableUsage(Expr *p){
unsigned int mask = 0;
if( p==0 ) return 0;
if( p->op==TK_FIELD ){
return 1<<p->iTable;
}
if( p->pRight ){
mask = exprTableUsage(p->pRight);
}
if( p->pLeft ){
mask |= exprTableUsage(p->pLeft);
}
return mask;
}
/*
** The input to this routine is an ExprInfo structure with only the
** "p" field filled in. The job of this routine is to analyze the
** subexpression and populate all the other fields of the ExprInfo
** structure.
*/
static void exprAnalyze(ExprInfo *pInfo){
Expr *pExpr = pInfo->p;
pInfo->prereqLeft = exprTableUsage(pExpr->pLeft);
pInfo->prereqRight = exprTableUsage(pExpr->pRight);
pInfo->indexable = 0;
pInfo->idxLeft = -1;
pInfo->idxRight = -1;
if( pExpr->op==TK_EQ && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
if( pExpr->pRight->op==TK_FIELD ){
pInfo->idxRight = pExpr->pRight->iTable;
pInfo->indexable = 1;
}
if( pExpr->pLeft->op==TK_FIELD ){
pInfo->idxLeft = pExpr->pLeft->iTable;
pInfo->indexable = 1;
}
}
}
/*
** Generating the beginning of the loop used for WHERE clause processing.
** The return value is a pointer to an (opaque) structure that contains
** information needed to terminate the loop. Later, the calling routine
** should invoke sqliteWhereEnd() with the return value of this function
** in order to complete the WHERE clause processing.
**
** If an error occurs, this routine returns NULL.
*/
WhereInfo *sqliteWhereBegin(
Parse *pParse, /* The parser context */
IdList *pTabList, /* A list of all tables */
Expr *pWhere, /* The WHERE clause */
int pushKey /* If TRUE, leave the table key on the stack */
){
int i; /* Loop counter */
WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
int brk, cont; /* Addresses used during code generation */
int *aOrder; /* Order in which pTabList entries are searched */
int nExpr; /* Number of subexpressions in the WHERE clause */
int loopMask; /* One bit set for each outer loop */
int haveKey; /* True if KEY is on the stack */
Index *aIdx[32]; /* Index to use on each nested loop. */
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
/* Allocate space for aOrder[]. */
aOrder = sqliteMalloc( sizeof(int) * pTabList->nId );
/* Allocate and initialize the WhereInfo structure that will become the
** return value.
*/
pWInfo = sqliteMalloc( sizeof(WhereInfo) );
if( pWInfo==0 ){
sqliteFree(aOrder);
return 0;
}
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
/* Split the WHERE clause into as many as 32 separate subexpressions
** where each subexpression is separated by an AND operator. Any additional
** subexpressions are attached in the aExpr[32] and will not enter
** into the query optimizer computations. 32 is chosen as the cutoff
** since that is the number of bits in an integer that we use for an
** expression-used mask.
*/
memset(aExpr, 0, sizeof(aExpr));
nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
/* Analyze all of the subexpressions.
*/
for(i=0; i<nExpr; i++){
exprAnalyze(&aExpr[i]);
}
/* Figure out a good nesting order for the tables. aOrder[0] will
** be the index in pTabList of the outermost table. aOrder[1] will
** be the first nested loop and so on. aOrder[pTabList->nId-1] will
** be the innermost loop.
**
** Someday will put in a good algorithm here to reorder to the loops
** for an effiecient query. But for now, just use whatever order the
** tables appear in in the pTabList.
*/
for(i=0; i<pTabList->nId; i++){
aOrder[i] = i;
}
/* Figure out what index to use (if any) for each nested loop.
** Make aIdx[i] point to the index to use for the i-th nested loop
** where i==0 is the outer loop and i==pTabList->nId-1 is the inner
** loop.
**
** Actually, if there are more than 32 tables in the join, only the
** first 32 tables are candidates for indices.
*/
loopMask = 0;
for(i=0; i<pTabList->nId && i<ARRAYSIZE(aIdx); i++){
int idx = aOrder[i];
Table *pTab = pTabList->a[idx].pTab;
Index *pIdx;
Index *pBestIdx = 0;
/* Do a search for usable indices. Leave pBestIdx pointing to
** most specific usable index.
**
** "Most specific" means that pBestIdx is the usable index that
** has the largest value for nField. A usable index is one for
** which there are subexpressions to compute every field of the
** index.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int j;
int fieldMask = 0;
if( pIdx->nField>32 ) continue;
for(j=0; j<nExpr; j++){
if( aExpr[j].idxLeft==idx
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
int iField = aExpr[j].p->pLeft->iField;
int k;
for(k=0; k<pIdx->nField; k++){
if( pIdx->aiField[k]==iField ){
fieldMask |= 1<<k;
break;
}
}
}
if( aExpr[j].idxRight==idx
&& (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
int iField = aExpr[j].p->pRight->iField;
int k;
for(k=0; k<pIdx->nField; k++){
if( pIdx->aiField[k]==iField ){
fieldMask |= 1<<k;
break;
}
}
}
}
if( fieldMask + 1 == (1<<pIdx->nField) ){
if( pBestIdx==0 || pBestIdx->nField<pIdx->nField ){
pBestIdx = pIdx;
}
}
}
aIdx[i] = pBestIdx;
}
/* Open all tables in the pTabList and all indices in aIdx[].
*/
for(i=0; i<pTabList->nId; i++){
sqliteVdbeAddOp(v, OP_Open, i, 0, pTabList->a[i].pTab->zName, 0);
if( i<ARRAYSIZE(aIdx) && aIdx[i]!=0 ){
sqliteVdbeAddOp(v, OP_Open, pTabList->nId+i, 0, aIdx[i]->zName, 0);
}
}
/* Generate the code to do the search
*/
pWInfo->iBreak = brk = sqliteVdbeMakeLabel(v);
loopMask = 0;
for(i=0; i<pTabList->nId; i++){
int j, k;
int idx = aOrder[i];
Index *pIdx = i<ARRAYSIZE(aIdx) ? aIdx[i] : 0;
cont = sqliteVdbeMakeLabel(v);
if( pIdx==0 ){
/* Case 1: There was no usable index. We must do a complete
** scan of the table.
*/
sqliteVdbeAddOp(v, OP_Next, idx, brk, 0, cont);
haveKey = 0;
}else{
/* Case 2: We do have a usable index in pIdx.
*/
for(j=0; j<pIdx->nField; j++){
for(k=0; k<nExpr; k++){
if( aExpr[k].p==0 ) continue;
if( aExpr[k].idxLeft==idx
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& aExpr[k].p->pLeft->iField==pIdx->aiField[j]
){
sqliteExprCode(pParse, aExpr[k].p->pRight);
aExpr[k].p = 0;
break;
}
if( aExpr[k].idxRight==idx
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
&& aExpr[k].p->pRight->iField==pIdx->aiField[j]
){
sqliteExprCode(pParse, aExpr[k].p->pLeft);
aExpr[k].p = 0;
break;
}
}
}
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Fetch, pTabList->nId+i, 0, 0, 0);
sqliteVdbeAddOp(v, OP_NextIdx, pTabList->nId+i, brk, 0, cont);
if( i==pTabList->nId-1 && pushKey ){
haveKey = 1;
}else{
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
haveKey = 0;
}
}
loopMask |= 1<<idx;
/* Insert code to test every subexpression that can be completely
** computed using the current set of tables.
*/
for(j=0; j<nExpr; j++){
if( aExpr[j].p==0 ) continue;
if( (aExpr[j].prereqRight & loopMask)!=aExpr[j].prereqRight ) continue;
if( (aExpr[j].prereqLeft & loopMask)!=aExpr[j].prereqLeft ) continue;
if( haveKey ){
sqliteVdbeAddOp(v, OP_Fetch, idx, 0, 0, 0);
haveKey = 0;
}
sqliteExprIfFalse(pParse, aExpr[j].p, cont);
aExpr[j].p = 0;
}
brk = cont;
}
pWInfo->iContinue = cont;
if( pushKey && !haveKey ){
sqliteVdbeAddOp(v, OP_Key, 0, 0, 0, 0);
}
sqliteFree(aOrder);
return pWInfo;
}
/*
** Generate the end of the WHERE loop.
*/
void sqliteWhereEnd(WhereInfo *pWInfo){
Vdbe *v = pWInfo->pParse->pVdbe;
sqliteVdbeAddOp(v, OP_Goto, 0, pWInfo->iContinue, 0, 0);
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, pWInfo->iBreak);
sqliteFree(pWInfo);
return;
}
/*
** Generate code into the current Vdbe to evaluate the given
** expression and leave the result on the stack.
*/
void sqliteExprCode(Parse *pParse, Expr *pExpr){
Vdbe *v = pParse->pVdbe;
int op;
switch( pExpr->op ){
case TK_PLUS: op = OP_Add; break;
case TK_MINUS: op = OP_Subtract; break;
case TK_STAR: op = OP_Multiply; break;
case TK_SLASH: op = OP_Divide; break;
case TK_AND: op = OP_And; break;
case TK_OR: op = OP_Or; break;
case TK_LT: op = OP_Lt; break;
case TK_LE: op = OP_Le; break;
case TK_GT: op = OP_Gt; break;
case TK_GE: op = OP_Ge; break;
case TK_NE: op = OP_Ne; break;
case TK_EQ: op = OP_Eq; break;
case TK_ISNULL: op = OP_IsNull; break;
case TK_NOTNULL: op = OP_NotNull; break;
case TK_NOT: op = OP_Not; break;
case TK_UMINUS: op = OP_Negative; break;
default: break;
}
switch( pExpr->op ){
case TK_FIELD: {
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
break;
}
case TK_INTEGER: {
int i = atoi(pExpr->token.z);
sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0);
break;
}
case TK_FLOAT: {
int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
break;
}
case TK_STRING: {
int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
sqliteVdbeDequoteP3(v, addr);
break;
}
case TK_NULL: {
sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
break;
}
case TK_AND:
case TK_OR:
case TK_PLUS:
case TK_STAR:
case TK_MINUS:
case TK_SLASH: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
break;
}
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
int dest;
sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
dest = sqliteVdbeCurrentAddr(v) + 2;
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
break;
}
case TK_NOT:
case TK_UMINUS: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
int dest;
sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
sqliteExprCode(pParse, pExpr->pLeft);
dest = sqliteVdbeCurrentAddr(v) + 2;
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
break;
}
}
return;
}
/*
** Generate code for a boolean expression such that a jump is made
** to the label "dest" if the expression is true but execution
** continues straight thru if the expression is false.
*/
void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
Vdbe *v = pParse->pVdbe;
int op = 0;
switch( pExpr->op ){
case TK_LT: op = OP_Lt; break;
case TK_LE: op = OP_Le; break;
case TK_GT: op = OP_Gt; break;
case TK_GE: op = OP_Ge; break;
case TK_NE: op = OP_Ne; break;
case TK_EQ: op = OP_Eq; break;
case TK_ISNULL: op = OP_IsNull; break;
case TK_NOTNULL: op = OP_NotNull; break;
default: break;
}
switch( pExpr->op ){
case TK_AND: {
int d2 = sqliteVdbeMakeLabel(v);
sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
sqliteVdbeResolveLabel(v, d2);
break;
}
case TK_OR: {
sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
sqliteExprIfTrue(pParse, pExpr->pRight, dest);
break;
}
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
default: {
sqliteExprCode(pParse, pExpr);
sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
break;
}
}
}
/*
** Generate code for boolean expression such that a jump is made
** to the label "dest" if the expression is false but execution
** continues straight thru if the expression is true.
*/
void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
Vdbe *v = pParse->pVdbe;
int op = 0;
switch( pExpr->op ){
case TK_LT: op = OP_Ge; break;
case TK_LE: op = OP_Gt; break;
case TK_GT: op = OP_Le; break;
case TK_GE: op = OP_Lt; break;
case TK_NE: op = OP_Eq; break;
case TK_EQ: op = OP_Ne; break;
case TK_ISNULL: op = OP_NotNull; break;
case TK_NOTNULL: op = OP_IsNull; break;
default: break;
}
switch( pExpr->op ){
case TK_AND: {
sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
break;
}
case TK_OR: {
int d2 = sqliteVdbeMakeLabel(v);
sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
sqliteExprIfFalse(pParse, pExpr->pRight, dest);
sqliteVdbeResolveLabel(v, d2);
break;
}
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
break;
}
default: {
sqliteExprCode(pParse, pExpr);
sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
break;
}
}
}

90
tool/gdbmdump.c Normal file
View File

@ -0,0 +1,90 @@
/*
** Copyright (c) 1999, 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** 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. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** A utility to dump the entire contents of a GDBM table in a
** readable format.
*/
#include <stdio.h>
#include <ctype.h>
#include <gdbm.h>
#include <stdlib.h>
static void print_data(char *zPrefix, datum p){
int i, j;
printf("%-5s: ", zPrefix);
for(i=0; i<p.dsize; i+=20){
for(j=i; j<p.dsize && j<i+20; j++){
printf("%02x", 0xff & p.dptr[j]);
if( (j&3)==3 ) printf(" ");
}
while( j<i+20 ){
printf(" ");
if( (j&3)==3 ) printf(" ");
j++;
}
printf(" ");
for(j=i; j<p.dsize && j<i+20; j++){
int c = p.dptr[j];
if( !isprint(c) ){ c = '.'; }
putchar(c);
}
printf("\n");
if( i+20<p.dsize ) printf(" ");
}
}
static int gdbm_dump(char *zFilename){
GDBM_FILE p;
datum data, key, next;
p = gdbm_open(zFilename, 0, GDBM_READER, 0, 0);
if( p==0 ){
fprintf(stderr,"can't open file \"%s\"\n", zFilename);
return 1;
}
key = gdbm_firstkey(p);
while( key.dptr ){
print_data("key",key);
data = gdbm_fetch(p, key);
if( data.dptr ){
print_data("data",data);
free( data.dptr );
}
next = gdbm_nextkey(p, key);
free( key.dptr );
key = next;
printf("\n");
}
gdbm_close(p);
return 0;
}
int main(int argc, char **argv){
int i;
int nErr = 0;
for(i=1; i<argc; i++){
nErr += gdbm_dump(argv[i]);
}
return nErr;
}

4021
tool/lemon.c Normal file

File diff suppressed because it is too large Load Diff

606
tool/lempar.c Normal file
View File

@ -0,0 +1,606 @@
/* Driver template for the LEMON parser generator.
** Copyright 1991-1995 by D. Richard Hipp.
*
* This version is specially modified for use with sqlite.
* @(#) $Id: lempar.c,v 1.1 2000/05/29 14:26:02 drh Exp $
*
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Library General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This library 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. See the GNU
** Library General Public License for more details.
**
** You should have received a copy of the GNU Library General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Modified 1997 to make it suitable for use with makeheaders.
*/
/* First off, code is include which follows the "include" declaration
** in the input 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.
** 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.
** ParseARGDECL is a declaration of a 3rd argument to the
** parser, or null if there is no extra argument.
** ParseKRARGDECL A version of ParseARGDECL for K&R C.
** ParseANSIARGDECL A version of ParseARGDECL for ANSI C.
** 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)
/* Next is the action table. Each entry in this table contains
**
** + An integer which is the number representing the look-ahead
** token
**
** + An integer indicating what action to take. Number (N) between
** 0 and YYNSTATE-1 mean shift the look-ahead and go to state N.
** Numbers between YYNSTATE and YYNSTATE+YYNRULE-1 mean reduce by
** rule N-YYNSTATE. Number YYNSTATE+YYNRULE means that a syntax
** error has occurred. Number YYNSTATE+YYNRULE+1 means the parser
** accepts its input.
**
** + A pointer to the next entry with the same hash value.
**
** The action table is really a series of hash tables. Each hash
** table contains a number of entries which is a power of two. The
** "state" table (which follows) contains information about the starting
** point and size of each hash table.
*/
struct yyActionEntry {
YYCODETYPE lookahead; /* The value of the look-ahead token */
YYACTIONTYPE action; /* Action to take for this look-ahead */
struct yyActionEntry *next; /* Next look-ahead with the same hash, or NULL */
};
static struct yyActionEntry yyActionTable[] = {
%%
};
/* The state table contains information needed to look up the correct
** action in the action table, given the current state of the parser.
** Information needed includes:
**
** + A pointer to the start of the action hash table in yyActionTable.
**
** + A mask used to hash the look-ahead token. The mask is an integer
** which is one less than the size of the hash table.
**
** + The default action. This is the action to take if no entry for
** the given look-ahead is found in the action hash table.
*/
struct yyStateEntry {
struct yyActionEntry *hashtbl; /* Start of the hash table in yyActionTable */
int mask; /* Mask used for hashing the look-ahead */
YYACTIONTYPE actionDefault; /* Default action if look-ahead not found */
};
static struct yyStateEntry yyStateTable[] = {
%%
};
/* 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 {
int stateno; /* The state-number */
int 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 */
};
/* The state of the parser is completely contained in an instance of
** the following structure */
struct yyParser {
int idx; /* Index of top element in stack */
int errcnt; /* Shifts left before out of the error */
struct yyStackEntry *top; /* Pointer to the top stack element */
struct yyStackEntry stack[YYSTACKDEPTH]; /* The parser's stack */
};
typedef struct yyParser yyParser;
#ifndef NDEBUG
#include <stdio.h>
static FILE *yyTraceFILE = 0;
static char *yyTracePrompt = 0;
/*
** 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.
*/
/* SQLITE MODIFICATION: Give the function file scope */
void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
yyTraceFILE = TraceFILE;
yyTracePrompt = zTracePrompt;
if( yyTraceFILE==0 ) yyTracePrompt = 0;
else if( yyTracePrompt==0 ) yyTraceFILE = 0;
}
/* For tracing shifts, the names of all terminals and nonterminals
** are required. The following table supplies these names */
static char *yyTokenName[] = {
%%
};
#define YYTRACE(X) if( yyTraceFILE ) fprintf(yyTraceFILE,"%sReduce [%s].\n",yyTracePrompt,X);
#else
#define YYTRACE(X)
#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.
*/
/* SQLITE MODIFICATION: Give the function file scope */
void *ParseAlloc(void *(*mallocProc)()){
yyParser *pParser;
pParser = (yyParser*)(*mallocProc)( sizeof(yyParser), __FILE__, __LINE__ );
if( pParser ){
pParser->idx = -1;
}
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(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
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;
if( pParser->idx<0 ) return 0;
#ifndef NDEBUG
if( yyTraceFILE && pParser->idx>=0 ){
fprintf(yyTraceFILE,"%sPopping %s\n",
yyTracePrompt,
yyTokenName[pParser->top->major]);
}
#endif
yymajor = pParser->top->major;
yy_destructor( yymajor, &pParser->top->minor);
pParser->idx--;
pParser->top--;
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>
*/
/* SQLITE MODIFICATION: Give the function file scope */
void ParseFree(
void *p, /* The parser to be deleted */
void (*freeProc)() /* Function used to reclaim memory */
){
yyParser *pParser = (yyParser*)p;
if( pParser==0 ) return;
while( pParser->idx>=0 ) yy_pop_parser_stack(pParser);
(*freeProc)(pParser, __FILE__, __LINE__);
}
/*
** Find the appropriate action for a parser given the look-ahead token.
**
** 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_parser_action(
yyParser *pParser, /* The parser */
int iLookAhead /* The look-ahead token */
){
struct yyStateEntry *pState; /* Appropriate entry in the state table */
struct yyActionEntry *pAction; /* Action appropriate for the look-ahead */
/* if( pParser->idx<0 ) return YY_NO_ACTION; */
pState = &yyStateTable[pParser->top->stateno];
if( iLookAhead!=YYNOCODE ){
pAction = &pState->hashtbl[iLookAhead & pState->mask];
while( pAction ){
if( pAction->lookahead==iLookAhead ) return pAction->action;
pAction = pAction->next;
}
}else if( pState->mask!=0 || pState->hashtbl->lookahead!=YYNOCODE ){
return YY_NO_ACTION;
}
return pState->actionDefault;
}
/*
** 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 ot the minor token to shift in */
){
yypParser->idx++;
yypParser->top++;
if( yypParser->idx>=YYSTACKDEPTH ){
yypParser->idx--;
yypParser->top--;
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
%%
return;
}
yypParser->top->stateno = yyNewState;
yypParser->top->major = yyMajor;
yypParser->top->minor = *yypMinor;
#ifndef NDEBUG
if( yyTraceFILE && yypParser->idx>0 ){
int i;
fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
for(i=1; i<=yypParser->idx; i++)
fprintf(yyTraceFILE," %s",yyTokenName[yypParser->stack[i].major]);
fprintf(yyTraceFILE,"\n");
}
#endif
}
/* The following table contains information about every rule that
** is used during the reduce.
*/
static 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(); /* 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 */
ParseANSIARGDECL
){
int yygoto; /* The next state */
int yyact; /* The next action */
YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
struct yyStackEntry *yymsp; /* The top of the parser's stack */
int yysize; /* Amount to pop the stack */
yymsp = yypParser->top;
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
** follows:
** case 0:
** YYTRACE("<text of the rule>");
** #line <lineno> <grammarfile>
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
%%
};
yygoto = yyRuleInfo[yyruleno].lhs;
yysize = yyRuleInfo[yyruleno].nrhs;
yypParser->idx -= yysize;
yypParser->top -= yysize;
yyact = yy_find_parser_action(yypParser,yygoto);
if( yyact < YYNSTATE ){
yy_shift(yypParser,yyact,yygoto,&yygotominor);
}else if( yyact == YYNSTATE + YYNRULE + 1 ){
yy_accept(yypParser ParseARGDECL);
}
}
/*
** The following code executes when the parse fails
*/
static void yy_parse_failed(
yyParser *yypParser /* The parser */
ParseANSIARGDECL /* Extra arguments (if any) */
){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
}
#endif
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser fails */
%%
}
/*
** 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 */
ParseANSIARGDECL /* Extra arguments (if any) */
){
#define TOKEN (yyminor.yy0)
%%
}
/*
** The following is executed when the parser accepts
*/
static void yy_accept(
yyParser *yypParser /* The parser */
ParseANSIARGDECL /* Extra arguments (if any) */
){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
}
#endif
while( yypParser->idx>=0 ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser accepts */
%%
}
/* 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.
*/
/* SQLITE MODIFICATION: Give the function file scope */
void Parse(
void *yyp, /* The parser */
int yymajor, /* The major token code number */
ParseTOKENTYPE yyminor /* The value for the token */
ParseANSIARGDECL
){
YYMINORTYPE yyminorunion;
int yyact; /* The parser action. */
int yyendofinput; /* True if we are at the end of input */
int yyerrorhit = 0; /* True if yymajor has invoked an error */
yyParser *yypParser; /* The parser */
/* (re)initialize the parser, if necessary */
yypParser = (yyParser*)yyp;
if( yypParser->idx<0 ){
if( yymajor==0 ) return;
yypParser->idx = 0;
yypParser->errcnt = -1;
yypParser->top = &yypParser->stack[0];
yypParser->top->stateno = 0;
yypParser->top->major = 0;
}
yyminorunion.yy0 = yyminor;
yyendofinput = (yymajor==0);
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
}
#endif
do{
yyact = yy_find_parser_action(yypParser,yymajor);
if( yyact<YYNSTATE ){
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
yypParser->errcnt--;
if( yyendofinput && yypParser->idx>=0 ){
yymajor = 0;
}else{
yymajor = YYNOCODE;
}
}else if( yyact < YYNSTATE + YYNRULE ){
yy_reduce(yypParser,yyact-YYNSTATE ParseARGDECL);
}else if( yyact == YY_ERROR_ACTION ){
#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->errcnt<0 ){
yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
}
if( yypParser->top->major==YYERRORSYMBOL || yyerrorhit ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
yy_destructor(yymajor,&yyminorunion);
yymajor = YYNOCODE;
}else{
while(
yypParser->idx >= 0 &&
yypParser->top->major != YYERRORSYMBOL &&
(yyact = yy_find_parser_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
){
yy_pop_parser_stack(yypParser);
}
if( yypParser->idx < 0 || yymajor==0 ){
yy_destructor(yymajor,&yyminorunion);
yy_parse_failed(yypParser ParseARGDECL);
yymajor = YYNOCODE;
}else if( yypParser->top->major!=YYERRORSYMBOL ){
YYMINORTYPE u2;
u2.YYERRSYMDT = 0;
yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
}
}
yypParser->errcnt = 3;
yyerrorhit = 1;
#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->errcnt<=0 ){
yy_syntax_error(yypParser,yymajor,yyminorunion ParseARGDECL);
}
yypParser->errcnt = 3;
yy_destructor(yymajor,&yyminorunion);
if( yyendofinput ){
yy_parse_failed(yypParser ParseARGDECL);
}
yymajor = YYNOCODE;
#endif
}else{
yy_accept(yypParser ParseARGDECL);
yymajor = YYNOCODE;
}
}while( yymajor!=YYNOCODE && yypParser->idx>=0 );
return;
}

23
tool/opNames.awk Normal file
View File

@ -0,0 +1,23 @@
# Read the sqliteVdbe.h file and generate a table of opcode names.
#
BEGIN {
printf "static char *zOpName[] = { 0,\n"
n = 0
}
/^#define OP_MAX/ {
next
}
/^#define OP_/ {
name = "\"" substr($2,4) "\","
if( n<3 ){
printf " %-16s", name
n++
} else {
printf " %s\n", name
n = 0
}
}
END {
if( n ){ printf "\n" }
printf "};\n"
}

23
tool/opcodeDoc.awk Normal file
View File

@ -0,0 +1,23 @@
#
# Extract opcode documentation for sqliteVdbe.c and generate HTML
#
BEGIN {
print "<html><body bgcolor=white>"
print "<h1>SQLite Virtual Database Engine Opcodes</h1>"
print "<table>"
}
/ Opcode: /,/\*\// {
if( $2=="Opcode:" ){
printf "<tr><td>%s&nbsp;%s&nbsp;%s&nbsp;%s</td>\n<td>\n", $3, $4, $5, $6
}else if( $1=="*/" ){
printf "</td></tr>\n"
}else if( NF>1 ){
sub(/^ *\*\* /,"")
gsub(/</,"&lt;")
gsub(/&/,"&amp;")
print
}
}
END {
print "</table></body></html>"
}

12
tool/renumberOps.awk Normal file
View File

@ -0,0 +1,12 @@
# Run this script on sqliteVdbe.h to renumber the opcodes sequentially.
#
BEGIN { cnt = 1 }
/^#define OP_MAX/ {
printf "#define %-20s %3d\n",$2, cnt-1
next
}
/^#define OP_/ {
printf "#define %-20s %3d\n",$2, cnt++
next
}
{ print }