first commit

This commit is contained in:
Souti
2025-03-06 11:09:58 +01:00
commit 11f7d440ff
330 changed files with 38306 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
2006-05-11 Michael Roth <mroth@nessie.de>
Version 0.4.1
* libluasqlite3.c: Fix to compile with MSVC6.
2006-05-09 Michael Roth <mroth@nessie.de>
Version 0.4
2006-04-12 Michael Roth <mroth@nessie.de>
* Makefile.in, configure.ac, configure: Switched to gnu
autoconf framework.
2006-04-09 Michael Roth <mroth@nessie.de>
* Makefile (clean): Documentation will no longer vaporize.
* libluasqlite3-loader.lua.in: loadlib Fix for lua-5.1.
* exmaples/*: Search path fixes for lua-5.1
2005-09-14 Michael Roth <mroth@nessie.de>
* sqlite3.lua (call_user_func): Fix for nil arguments in lua-5.1.
(stmt_class.first_cols): Dito but for return values.
* tests-sqlite3.lua (bug:test_nils): Testcase for nils in lua-5.1.
2005-06-15 Michael Roth <mroth@nessie.de>
Version 0.3:
* Documentation updates.
* examples/smart.lua: New.
* tests-sqlite3.lua: New test cases for parameter names without
leading dollar or colon sign.
New test cases for binding with name/value tables.
New test cases for stmt:parameter_names().
* sqlite3.lua (stmt_class.prepare): Support for parameter names
without leading dollar or colon sign.
(stmt_class.bind) Support for binding with name/value tables.
(stmt_class.parameter_names) new.
2005-06-14 Michael Roth <mroth@nessie.de>
* sqlite3.lua (stmt_class.prepare): Auto parameter mapping.
* libluasqlite3.c (l_sqlite3_bind_parameter_name_x): new.
2005-06-03 Michael Roth <mroth@nessie.de>
* sqlite3.lua (db_class.set_busy_timeout & set_busy_handler): new.
(db_class.set_trace_handler): Fixed error handling.
(stmt_rows): Fixed autoclose and reset behaviour (Bug #1).
2005-06-02 Michael Roth <mroth@nessie.de>
* tests-sqlite3.lua: New test case for reported bugs.
(bug:test_1): Bug #1, bind()/first_cols() problem on empty tables.
2004-10-07 Michael Roth <mroth@nessie.de>
Version 0.2:
* Lua-Sqlite3 should now compile with older compilers.
* Requires the sqlite library version 3.0.7.
2004-09-14 Michael Roth <mroth@nessie.de>
Version 0.1 (alpha):
* This is the first public release of Lua-Sqlite3.

32
lua-sqlite3-0.4.1/LICENSE Normal file
View File

@@ -0,0 +1,32 @@
Lua-Sqlite3 License
-------------------
Lua-Sqlite3 is written by Michael Roth <mroth@nessie.de> and is
licensed under the terms of the MIT license reproduced below.
========================================================================
Copyright (c) 2004, 2005, 2006 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================

View File

@@ -0,0 +1,139 @@
prefix := @prefix@
exec_prefix := @exec_prefix@
bindir := @bindir@
sbindir := @sbindir@
libdir := @libdir@
libexecdir := @libexecdir@
mandir := @mandir@
luadir := @libdir@/lua
install := install
install-data := $(install) -p -m 644
install-prog := $(install) -p -m 755
install-dirs := $(install) -d
LUA := @LUA@
CC := @CC@
CFLAGS := @CFLAGS@
LDFLAGS := @LDFLAGS@
LIBS := @LIBS@
COMPILE = $(CC) -c $(CFLAGS) -o $@ $<
LINK = $(CC) $(LDFLAGS) -o $@ $+ $(LIBS)
LINKSHARED = $(CC) -shared $(LDFLAGS) -o $@ $+ $(LIBS)
DESTDIR =
PACKAGE_NAME := lua-sqlite3
PACKAGE_VERSION := 0.4.1
LUA_FILES := sqlite3.lua luasql-sqlite3.lua
SOURCE_FILES := libluasqlite3.c
DOC_FILES := documentation.html README LICENSE
DIST_FILES := $(LUA_FILES) $(SOURCE_FILES) $(DOC_FILES) \
Makefile.in lunit.lua tests-luasql.lua \
tests-sqlite3.lua tests.lua \
libluasqlite3-loader.lua.in ChangeLog \
configure.ac configure
DIST_DIR := $(PACKAGE_NAME)-$(PACKAGE_VERSION)
CLEAN_FILES := libluasqlite3.o libluasqlite3.so \
libluasqlite3-loader.lua libluasqlite3-loader.lua.install
DESTDIR :=
all: libluasqlite3.so libluasqlite3-loader.lua libluasqlite3-loader.lua.install
install: all
$(install-dirs) $(DESTDIR)$(luadir)
$(install-data) $(LUA_FILES) libluasqlite3.so $(DESTDIR)$(luadir)
$(install-data) libluasqlite3-loader.lua.install $(DESTDIR)$(luadir)/libluasqlite3-loader.lua
uninstall:
( cd $(DESTDIR)$(luadir); rm -f $(LUA_FILES) libluasqlite3-loader.lua libluasqlite3.so )
clean:
rm -f $(CLEAN_FILES)
allclean: clean distclean docsclean
distdir: $(DIST_FILES) docs
rm -rf $(DIST_DIR)
mkdir $(DIST_DIR) $(DIST_DIR)/examples
cp -a -L $(DIST_FILES) $(DIST_DIR)
cp -a -L examples/*.lua $(DIST_DIR)/examples
find $(DIST_DIR) -perm +444 -exec chmod a+w {} \;
find $(DIST_DIR) -perm +222 -exec chmod a+r {} \;
find $(DIST_DIR) -perm +111 -exec chmod a+x {} \;
chmod -R a-st $(DIST_DIR)
chmod -R go-w $(DIST_DIR)
chmod 755 $(DIST_DIR)
touch distdir
dist-tar: distdir
tar -c -f $(DIST_DIR).tar --owner=root --group=root $(DIST_DIR)
touch dist-tar
dist-bz2: dist-tar
rm -f $(DIST_DIR).tar.gz
gzip -9 -c $(DIST_DIR).tar > $(DIST_DIR).tar.gz
touch dist-bz2
dist-gz: dist-tar
rm -f $(DIST_DIR).tar.bz2
bzip2 -9 -c $(DIST_DIR).tar > $(DIST_DIR).tar.bz2
touch dist-gz
dist-zip: distdir
zip -rq $(DIST_DIR).zip $(DIST_DIR)
touch dist-zip
dist: dist-bz2 dist-gz dist-zip
distclean:
rm -rf $(DIST_DIR)
rm -f $(DIST_DIR).tar $(DIST_DIR).tar.gz $(DIST_DIR).tar.bz2 $(DIST_DIR).zip
rm -f distdir dist-tar dist-gz dist-bz2 dist-zip
devel:
./make-devel.sh
develclean:
rm -rf obj-5.0 obj-5.1
docs: documentation.html
docsclean:
rm -f documentation.html
check: all
$(LUA) tests.lua
documentation.html: docs/doc.mrd makedoc.lua
$(LUA) makedoc.lua >documentation.html
libluasqlite3.o: libluasqlite3.c
$(COMPILE)
libluasqlite3.so: libluasqlite3.o
$(LINKSHARED)
libluasqlite3-loader.lua.install: libluasqlite3-loader.lua.in
m4 -DSHARED_LIB_PATH=$(luadir) \
< libluasqlite3-loader.lua.in > libluasqlite3-loader.lua.install
libluasqlite3-loader.lua: libluasqlite3-loader.lua.in
m4 -DSHARED_LIB_PATH=$(shell pwd) \
< libluasqlite3-loader.lua.in > libluasqlite3-loader.lua

19
lua-sqlite3-0.4.1/README Normal file
View File

@@ -0,0 +1,19 @@
This is Lua-Sqlite3 release 0.4.1.
Lua-Sqlite3 is a sqlite3 wrapper for lua.
To learn more about lua-sqlite3 take a look in documentation.html.
Edit Makefile.cfg to match your environment.
Please note that this release is still alpha software. This mean that
there exists a chance that function signatures and behavour will change
in the future.
If you have suggestions, questions or feature request please
feel free to contact me.
Michael Roth <mroth@nessie.de>

View File

@@ -0,0 +1,457 @@
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by lua-sqlite3 configure 0.4, which was
generated by GNU Autoconf 2.59. Invocation command line was
$ ./configure
## --------- ##
## Platform. ##
## --------- ##
hostname = domoticz
uname -m = armv7l
uname -r = 4.1.13-v7+
uname -s = Linux
uname -v = #826 SMP PREEMPT Fri Nov 13 20:19:03 GMT 2015
/usr/bin/uname -p = unknown
/bin/uname -X = unknown
/bin/arch = unknown
/usr/bin/arch -k = unknown
/usr/convex/getsysinfo = unknown
hostinfo = unknown
/bin/machine = unknown
/usr/bin/oslevel = unknown
/bin/universe = unknown
PATH: /usr/local/sbin
PATH: /usr/local/bin
PATH: /usr/sbin
PATH: /usr/bin
PATH: /sbin
PATH: /bin
## ----------- ##
## Core tests. ##
## ----------- ##
configure:1373: checking for gcc
configure:1389: found /usr/bin/gcc
configure:1399: result: gcc
configure:1643: checking for C compiler version
configure:1646: gcc --version </dev/null >&5
gcc (Debian 4.6.3-14+rpi1) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
configure:1649: $? = 0
configure:1651: gcc -v </dev/null >&5
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.3-14+rpi1' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-sjlj-exceptions --with-arch=armv6 --with-fpu=vfp --with-float=hard --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
Thread model: posix
gcc version 4.6.3 (Debian 4.6.3-14+rpi1)
configure:1654: $? = 0
configure:1656: gcc -V </dev/null >&5
gcc: error: unrecognized option '-V'
gcc: fatal error: no input files
compilation terminated.
configure:1659: $? = 4
configure:1682: checking for C compiler default output file name
configure:1685: gcc conftest.c >&5
configure:1688: $? = 0
configure:1734: result: a.out
configure:1739: checking whether the C compiler works
configure:1745: ./a.out
configure:1748: $? = 0
configure:1765: result: yes
configure:1772: checking whether we are cross compiling
configure:1774: result: no
configure:1777: checking for suffix of executables
configure:1779: gcc -o conftest conftest.c >&5
configure:1782: $? = 0
configure:1807: result:
configure:1813: checking for suffix of object files
configure:1834: gcc -c conftest.c >&5
configure:1837: $? = 0
configure:1859: result: o
configure:1863: checking whether we are using the GNU C compiler
configure:1887: gcc -c conftest.c >&5
configure:1893: $? = 0
configure:1897: test -z
|| test ! -s conftest.err
configure:1900: $? = 0
configure:1903: test -s conftest.o
configure:1906: $? = 0
configure:1919: result: yes
configure:1925: checking whether gcc accepts -g
configure:1946: gcc -c -g conftest.c >&5
configure:1952: $? = 0
configure:1956: test -z
|| test ! -s conftest.err
configure:1959: $? = 0
configure:1962: test -s conftest.o
configure:1965: $? = 0
configure:1976: result: yes
configure:1993: checking for gcc option to accept ANSI C
configure:2063: gcc -c -g -O2 conftest.c >&5
configure:2069: $? = 0
configure:2073: test -z
|| test ! -s conftest.err
configure:2076: $? = 0
configure:2079: test -s conftest.o
configure:2082: $? = 0
configure:2100: result: none needed
configure:2118: gcc -c -g -O2 conftest.c >&5
conftest.c:2:3: error: unknown type name 'choke'
conftest.c:2:3: error: expected '=', ',', ';', 'asm' or '__attribute__' at end of input
configure:2124: $? = 1
configure: failed program was:
| #ifndef __cplusplus
| choke me
| #endif
configure:2263: checking how to run the C preprocessor
configure:2298: gcc -E conftest.c
configure:2304: $? = 0
configure:2336: gcc -E conftest.c
conftest.c:9:28: fatal error: ac_nonexistent.h: No such file or directory
compilation terminated.
configure:2342: $? = 1
configure: failed program was:
| /* confdefs.h. */
|
| #define PACKAGE_NAME "lua-sqlite3"
| #define PACKAGE_TARNAME "lua-sqlite3"
| #define PACKAGE_VERSION "0.4"
| #define PACKAGE_STRING "lua-sqlite3 0.4"
| #define PACKAGE_BUGREPORT "mroth@nessie.de"
| /* end confdefs.h. */
| #include <ac_nonexistent.h>
configure:2381: result: gcc -E
configure:2405: gcc -E conftest.c
configure:2411: $? = 0
configure:2443: gcc -E conftest.c
conftest.c:9:28: fatal error: ac_nonexistent.h: No such file or directory
compilation terminated.
configure:2449: $? = 1
configure: failed program was:
| /* confdefs.h. */
|
| #define PACKAGE_NAME "lua-sqlite3"
| #define PACKAGE_TARNAME "lua-sqlite3"
| #define PACKAGE_VERSION "0.4"
| #define PACKAGE_STRING "lua-sqlite3 0.4"
| #define PACKAGE_BUGREPORT "mroth@nessie.de"
| /* end confdefs.h. */
| #include <ac_nonexistent.h>
configure:2493: checking for egrep
configure:2503: result: grep -E
configure:2508: checking for ANSI C header files
configure:2533: gcc -c -g -O2 conftest.c >&5
configure:2539: $? = 0
configure:2543: test -z
|| test ! -s conftest.err
configure:2546: $? = 0
configure:2549: test -s conftest.o
configure:2552: $? = 0
configure:2641: gcc -o conftest -g -O2 conftest.c >&5
conftest.c: In function 'main':
conftest.c:26:7: warning: incompatible implicit declaration of built-in function 'exit' [enabled by default]
configure:2644: $? = 0
configure:2646: ./conftest
configure:2649: $? = 0
configure:2664: result: yes
configure:2688: checking for sys/types.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for sys/stat.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for stdlib.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for string.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for memory.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for strings.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for inttypes.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for stdint.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2688: checking for unistd.h
configure:2704: gcc -c -g -O2 conftest.c >&5
configure:2710: $? = 0
configure:2714: test -z
|| test ! -s conftest.err
configure:2717: $? = 0
configure:2720: test -s conftest.o
configure:2723: $? = 0
configure:2734: result: yes
configure:2895: checking for egrep
configure:2905: result: grep -E
configure:2912: checking whether linking with rpath is requested
configure:2922: result: no
configure:2970: checking for lua
configure:2989: found /usr/bin/lua
configure:3001: result: /usr/bin/lua
configure:3083: checking lua.h usability
configure:3095: gcc -c -g -O2 conftest.c >&5
conftest.c:53:17: fatal error: lua.h: No such file or directory
compilation terminated.
configure:3101: $? = 1
configure: failed program was:
| /* confdefs.h. */
|
| #define PACKAGE_NAME "lua-sqlite3"
| #define PACKAGE_TARNAME "lua-sqlite3"
| #define PACKAGE_VERSION "0.4"
| #define PACKAGE_STRING "lua-sqlite3 0.4"
| #define PACKAGE_BUGREPORT "mroth@nessie.de"
| #define STDC_HEADERS 1
| #define HAVE_SYS_TYPES_H 1
| #define HAVE_SYS_STAT_H 1
| #define HAVE_STDLIB_H 1
| #define HAVE_STRING_H 1
| #define HAVE_MEMORY_H 1
| #define HAVE_STRINGS_H 1
| #define HAVE_INTTYPES_H 1
| #define HAVE_STDINT_H 1
| #define HAVE_UNISTD_H 1
| /* end confdefs.h. */
| #include <stdio.h>
| #if HAVE_SYS_TYPES_H
| # include <sys/types.h>
| #endif
| #if HAVE_SYS_STAT_H
| # include <sys/stat.h>
| #endif
| #if STDC_HEADERS
| # include <stdlib.h>
| # include <stddef.h>
| #else
| # if HAVE_STDLIB_H
| # include <stdlib.h>
| # endif
| #endif
| #if HAVE_STRING_H
| # if !STDC_HEADERS && HAVE_MEMORY_H
| # include <memory.h>
| # endif
| # include <string.h>
| #endif
| #if HAVE_STRINGS_H
| # include <strings.h>
| #endif
| #if HAVE_INTTYPES_H
| # include <inttypes.h>
| #else
| # if HAVE_STDINT_H
| # include <stdint.h>
| # endif
| #endif
| #if HAVE_UNISTD_H
| # include <unistd.h>
| #endif
| #include <lua.h>
configure:3124: result: no
configure:3128: checking lua.h presence
configure:3138: gcc -E conftest.c
conftest.c:19:17: fatal error: lua.h: No such file or directory
compilation terminated.
configure:3144: $? = 1
configure: failed program was:
| /* confdefs.h. */
|
| #define PACKAGE_NAME "lua-sqlite3"
| #define PACKAGE_TARNAME "lua-sqlite3"
| #define PACKAGE_VERSION "0.4"
| #define PACKAGE_STRING "lua-sqlite3 0.4"
| #define PACKAGE_BUGREPORT "mroth@nessie.de"
| #define STDC_HEADERS 1
| #define HAVE_SYS_TYPES_H 1
| #define HAVE_SYS_STAT_H 1
| #define HAVE_STDLIB_H 1
| #define HAVE_STRING_H 1
| #define HAVE_MEMORY_H 1
| #define HAVE_STRINGS_H 1
| #define HAVE_INTTYPES_H 1
| #define HAVE_STDINT_H 1
| #define HAVE_UNISTD_H 1
| /* end confdefs.h. */
| #include <lua.h>
configure:3164: result: no
configure:3199: checking for lua.h
configure:3206: result: no
configure:3213: error: Cannot find lua.h
## ---------------- ##
## Cache variables. ##
## ---------------- ##
ac_cv_c_compiler_gnu=yes
ac_cv_env_CC_set=
ac_cv_env_CC_value=
ac_cv_env_CFLAGS_set=
ac_cv_env_CFLAGS_value=
ac_cv_env_CPPFLAGS_set=
ac_cv_env_CPPFLAGS_value=
ac_cv_env_CPP_set=
ac_cv_env_CPP_value=
ac_cv_env_LDFLAGS_set=
ac_cv_env_LDFLAGS_value=
ac_cv_env_build_alias_set=
ac_cv_env_build_alias_value=
ac_cv_env_host_alias_set=
ac_cv_env_host_alias_value=
ac_cv_env_target_alias_set=
ac_cv_env_target_alias_value=
ac_cv_exeext=
ac_cv_header_inttypes_h=yes
ac_cv_header_lua_h=no
ac_cv_header_memory_h=yes
ac_cv_header_stdc=yes
ac_cv_header_stdint_h=yes
ac_cv_header_stdlib_h=yes
ac_cv_header_string_h=yes
ac_cv_header_strings_h=yes
ac_cv_header_sys_stat_h=yes
ac_cv_header_sys_types_h=yes
ac_cv_header_unistd_h=yes
ac_cv_objext=o
ac_cv_path_LUA=/usr/bin/lua
ac_cv_prog_CPP='gcc -E'
ac_cv_prog_ac_ct_CC=gcc
ac_cv_prog_cc_g=yes
ac_cv_prog_cc_stdc=
ac_cv_prog_egrep='grep -E'
## ----------------- ##
## Output variables. ##
## ----------------- ##
CC='gcc'
CFLAGS='-g -O2'
CPP='gcc -E'
CPPFLAGS=''
DEFS=''
ECHO_C=''
ECHO_N='-n'
ECHO_T=''
EGREP='grep -E'
EXEEXT=''
LDFLAGS=''
LIBOBJS=''
LIBS=''
LTLIBOBJS=''
LUA='/usr/bin/lua'
OBJEXT='o'
PACKAGE_BUGREPORT='mroth@nessie.de'
PACKAGE_NAME='lua-sqlite3'
PACKAGE_STRING='lua-sqlite3 0.4'
PACKAGE_TARNAME='lua-sqlite3'
PACKAGE_VERSION='0.4'
PATH_SEPARATOR=':'
SHELL='/bin/bash'
ac_ct_CC='gcc'
bindir='${exec_prefix}/bin'
build_alias=''
datadir='${prefix}/share'
exec_prefix='NONE'
host_alias=''
includedir='${prefix}/include'
infodir='${prefix}/info'
libdir='${exec_prefix}/lib'
libexecdir='${exec_prefix}/libexec'
localstatedir='${prefix}/var'
mandir='${prefix}/man'
oldincludedir='/usr/include'
prefix='NONE'
program_transform_name='s,x,x,'
sbindir='${exec_prefix}/sbin'
sharedstatedir='${prefix}/com'
sysconfdir='${prefix}/etc'
target_alias=''
## ----------- ##
## confdefs.h. ##
## ----------- ##
#define HAVE_INTTYPES_H 1
#define HAVE_MEMORY_H 1
#define HAVE_STDINT_H 1
#define HAVE_STDLIB_H 1
#define HAVE_STRINGS_H 1
#define HAVE_STRING_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_UNISTD_H 1
#define PACKAGE_BUGREPORT "mroth@nessie.de"
#define PACKAGE_NAME "lua-sqlite3"
#define PACKAGE_STRING "lua-sqlite3 0.4"
#define PACKAGE_TARNAME "lua-sqlite3"
#define PACKAGE_VERSION "0.4"
#define STDC_HEADERS 1
configure: exit 1

4490
lua-sqlite3-0.4.1/configure vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
AC_INIT(lua-sqlite3, 0.4, mroth@nessie.de)
AC_COPYRIGHT([Copyright (c) 2006 Michael Roth <mroth@nessie.de>])
AC_PROG_CC()
AC_CHECK_HEADERS()
AC_PROG_EGREP()
AC_MSG_CHECKING([whether linking with rpath is requested])
AC_ARG_ENABLE(rpath,
AC_HELP_STRING([--enable-rpath], [link with rpath option]),
rpath=$enableval,
rpath=no
)
AC_MSG_RESULT($rpath)
lua_dir=""
lua_includedir=""
lua_libdir=""
LUA=""
AC_ARG_WITH(lua-dir,
[AC_HELP_STRING(--with-lua-dir=DIR, [where you installed lua [EPREFIX]]) ],
[
lua_dir="${withval}"
lua_includedir="${lua_dir}/include"
lua_libdir="${lua_dir}/lib"
LUA="${lua_dir}/bin/lua"
]
)
AC_ARG_WITH(lua-includedir,
[AC_HELP_STRING(--with-lua-includedir=DIR, [where to find lua headers [LUA_DIR/include]])],
lua_includedir="${withval}"
)
AC_ARG_WITH(lua-libdir,
[AC_HELP_STRING(--with-lua-libdir=DIR, [where to find the lua library [LUA_DIR/lib]])],
lua_libdir="${withval}"
)
AC_ARG_WITH(lua,
[AC_HELP_STRING(--with-lua=FILE, [path to the 'lua' command [LUA_DIR/bin/lua]])],
LUA="${withval}"
)
AC_PATH_PROG(LUA, lua, [], [$bindir:$exec_prefix/bin:$prefix/bin:$PATH])
if test ! -x "${LUA}" ; then
AC_MSG_WARN([No lua interpreter found!])
fi
sqlite3_dir=""
sqlite3_includedir=""
sqlite3_libdir=""
AC_ARG_WITH(sqlite3-dir,
[AC_HELP_STRING(--with-sqlite3-dir=DIR, [where you installed sqlite3 [EPREFIX]])],
[
sqlite3_dir="${withval}"
sqlite3_includedir="${sqlite3_dir}/include"
sqlite3_libdir="${sqlite3_dir}/lib"
]
)
AC_ARG_WITH(sqlite3-includedir,
[AC_HELP_STRING(--with-sqlite3-includedir=DIR, [where to find sqlite3 headers [SQLITE3_DIR/include]])],
sqlite3_includedir="${withval}"
)
AC_ARG_WITH(sqlite3-libdir,
[AC_HELP_STRING(--with-sqlite3-libdir=DIR, [where to find the sqlite3 library [SQLITE3_DIR/lib]])],
sqlite3_libdir="${withval}"
)
if test -n "$lua_includedir" ; then
CFLAGS="-I$lua_includedir $CFLAGS"
CPPFLAGS="-I$lua_includedir $CPPFLAGS"
fi
if test -n "$lua_libdir" ; then
if test $rpath = yes ; then
LDFLAGS="-Wl,-rpath,${lua_libdir}"
fi
LDFLAGS="-L$lua_libdir $LDFLAGS"
fi
if test -n "$sqlite3_includedir" -a "$sqlite3_includedir" != "$lua_includedir"; then
CFLAGS="-I$sqlite3_includedir $CFLAGS"
CPPFLAGS="-I$sqlite3_includedir $CPPFLAGS"
fi
if test -n "$sqlite3_libdir" -a "$sqlite3_libdir" != "$lua_libdir"; then
if test $rpath = yes ; then
LDFLAGS="-Wl,-rpath,${sqlite3_libdir}"
fi
LDFLAGS="-L$sqlite3_libdir $LDFLAGS"
fi
AC_CHECK_HEADER(lua.h, [], [AC_MSG_ERROR(Cannot find lua.h)])
AC_CHECK_HEADER(lauxlib.h, [], [AC_MSG_ERROR(Cannot find lauxlib.h)])
AC_CHECK_HEADER(sqlite3.h, [], [AC_MSG_ERROR(Cannot find sqlite3.h)])
AC_MSG_CHECKING(if we need -llua)
need_llua=yes
nm -D "$LUA" | $EGREP "T lua_open$" >/dev/null && need_llua=no
AC_MSG_RESULT($need_llua)
if test $need_llua = yes; then
LIBS="-llua -lm $LIBS"
fi
AC_MSG_CHECKING(if we need -llualib)
if test $need_llua = yes; then
AC_LINK_IFELSE([
#include "lauxlib.h"
int main()
{
luaL_Buffer buf;
luaL_buffinit(0, &buf);
return 0;
}
], [need_llualib=no], [need_llualib=yes])
else
need_llualib=no
fi
AC_MSG_RESULT($need_llualib)
if test $need_llualib = yes; then
LIBS="-llualib $LIBS"
fi
LIBS="-lsqlite3 $LIBS"
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@@ -0,0 +1,796 @@
<html><head><title>Lua-Sqlite3 Documentation</title></head>
<body>
<center><h1>Lua-Sqlite3 Documentation</h1></center>
<hr size="1">
<h1>Table of Contents</h1>
<blockquote><ul>
<li><a href="#ref1">Overview</a></li>
<ul>
<li><a href="#ref2">Summary</a></li>
</ul>
<li><a href="#ref3">Installation</a></li>
<li><a href="#ref4">Sqlite3 Frontend</a></li>
<ul>
<li><a href="#ref5">Error handling</a></li>
<li><a href="#ref6">Open and Close </a></li>
<li><a href="#ref7">Executing simple SQL Statements</a></li>
<li><a href="#ref8">Fetching Rows using SELECT Statements</a></li>
<li><a href="#ref9">Fetching a Single Row</a></li>
<li><a href="#ref10">Simple Prepared Statements</a></li>
<li><a href="#ref11">Prepared Statements with Parameters (Binding)</a></li>
<ul>
<li><a href="#ref12">Anonymous Parameters</a></li>
<li><a href="#ref13">Named Parameters</a></li>
<li><a href="#ref14">Automatic Parameter Name Mapping</a></li>
<li><a href="#ref15">Parameter Passing with a Name/Value Table</a></li>
<li><a href="#ref16">Querying Parameter Names </a></li>
</ul>
<li><a href="#ref17">Defining User Functions</a></li>
<li><a href="#ref18">Defining User Aggregates</a></li>
<li><a href="#ref19">Collatores and Collation Handler</a></li>
<li><a href="#ref20">Busy Timeout and Handler</a></li>
<ul>
<li><a href="#ref21">Busy Timeout</a></li>
<li><a href="#ref22">Busy Handler</a></li>
</ul>
<li><a href="#ref23">Progress, Authorizer, Trace and Commit Handler</a></li>
<ul>
<li><a href="#ref24">Trace Handler</a></li>
</ul>
<li><a href="#ref25">Call Chaining</a></li>
</ul>
<li><a href="#ref26">LuaSQL Frontend</a></li>
<li><a href="#ref27">libluasqlite3 Wrapper</a></li>
</ul></blockquote>
<hr size="1">
<h1><a name="ref1">Overview</a></h1><blockquote>
<p class="text">
Lua-Sqlite3 is a binding of sqlite (<a href="http://www.sqlite.org">http://www.sqlite.org</a>) Version 3 to Lua (<a href="http://www.lua.org/">http://www.lua.org/</a>).
</p>
<p class="text">
Lua-Sqlite3 is unique in contrast to other database bindings that it consists of two layers.
</p>
<p class="text">
The first layer, written in C, is called the 'backend'. The backend translates the C-api of the sqlite library version 3 to lua. The backend mainly converts datatypes from C to lua and vice versa and expose most C functions from the sqlite library to lua.
</p>
<p class="text">
The second layers are called 'frontends' and are written in lua. A frontend provides a specific view and interface to a database. For example it's the job of a frontend to translate error codes from the sqlite library to error messages in lua. It's also the job of a fronted to arrange for garbage collection of database handles and to make sure, that the functions exposed by the backend are called in the right order.
</p>
<p class="text">
Currently, Lua-Sqlite3 implements two frontends:
</p>
<ol>
<li>
An specialiced frontend, simple named 'Sqlite3', which explodes all capabilities of the sqlite library in a comfortable way.
</li>
<li>
A frontend, named 'LuaSQL-Sqlite3' which provides a LuaSQL compatible interface as used in the Kepler Project.
</li>
</ol>
<p class="text">
The first frontend named 'Sqlite3' provides following advantages:
</p>
<ul>
<li>
Works in host programs that use multiple Lua states.
</li>
<li>
No problems in conjunction with Lua coroutines, instead powerfull usage of coroutines possible.
</li>
<li>
Reading result sets using for-loop iterators.
</li>
<li>
Automatic and smart converting of data types.
</li>
<li>
Blocks of multiple SQL statements could be executed at once.
</li>
<li>
User functions, aggregates and collators in a natural way.
</li>
<li>
Authorize, trace, commit, progress and busy handlers.
</li>
</ul>
<p class="text">
The second frontend named 'LuaSQL-Sqlite3' is fully compatible with the database standard defined by the Kepler Project. This frontend provides currently no extensions.
</p>
</blockquote>
<h2><a name="ref2">Summary</a></h2><blockquote>
<p class="text">
The complete package is named 'Lua-Sqlite3'. It consists of a backend, named 'libluasqlite3' and currently two frontends, named 'Sqlite3' and 'LuaSQL-Sqlite3'.
</p>
<p class="text">
The backend is a thin wrapper to expose the C-api of sqlite library version 3 to lua. The backend is written in C.
</p>
<p class="text">
The frontends are build on top of the backend and are written in lua. The frontends provide a specific interface to a database.
</p>
</blockquote>
<hr size="1">
<h1><a name="ref3">Installation</a></h1><blockquote>
<p class="text">
Lua-Sqlite3 comes with a configure script to customize the paths where to install and where to find headers and libraries.
</p>
<p class="text">
The configure options --with-lua-dir and --with-sqlite3-dir tell where you installed lua and sqlite3 to search for headers and libraries.
</p>
<p class="text">
If you are using shared libraries and didn't installed sqlite3 or lua to a standard directory searched by the dynamic linker of your operating system, you could run configure with --enable-rpath to make sure, that Lua-Sqlite3 will find the shared libraries without setting LD_LIBRARY_PATH.
</p>
<p class="text">
After you run configure, run make to build the C-library. After successfull build of the C-library you could also run the provided testsuite to make sure all work as expected and finally install the package.
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
# ./configure --help
# ./configure ...
# make all
# make check
# make install
</pre>
<p class="text">
If you plan to use Lua-Sqlite3 on Windows or if you would like to link Lua-Sqlite3 statically to your host program, you should fiddle with the backend loader, a lua function named load_libluasqlite3() defined in the file libluasqlite3-loader.lua. Actually this file is created from a template, called libluasqlite3-loader.lua.in. The changes needed to the loader should be obvious.
</p>
<p class="text">
Warning: Maybe the loader process and behaviour will change in future releases again.
</p>
<p class="text">
Warning 2: The loader process will definitively change when Lua-Sqlite3 drops support for Lua-5.0 while switching to Lua-5.1 package style.
</p>
</blockquote>
<hr size="1">
<h1><a name="ref4">Sqlite3 Frontend</a></h1><blockquote>
<p class="text">
The Lua-Sqlite3 frontend provides an object oriented view to a sqlite3 database. There are only two types of objects: A database object and a statement object. The objects itself are really tables, but that doesn't matters.
</p>
<p class="text">
A Database object is created when opening a database was successfull. A statement object is created when you compile a SQL statement using an open database object.
</p>
<p class="text">
These objects provide different methods that you call to perform the desired function. There are methods to close objects and methods to execute SQL statements.
</p>
<p class="text">
A method is always called with the colon syntax. An error is raised, if you use a dot ('.') instead a colon (':').for example:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
-- Ok
db:close()
-- Error! Don't do this!
db.close()
-- This works too, but doesn't look nice and is error prone
db.close(db)
</pre>
<p class="text">
However, there is an exception. The 'sqlite3' namespace isn't an object, it's just a namespace, a table with functions. You must use a dot ('.'), using a colon (':') results in undefined behaviour or raises an error.
</p>
</blockquote>
<h2><a name="ref5">Error handling</a></h2><blockquote>
<p class="text">
If a method on any Lua-Sqlite3 object was successfull, something is returned that evaluates to true, if used in an expression. Mostly these are newly created objects or returned rows.
</p>
<p class="text">
There are two possible error sources:
</p>
<ol>
<li>
Errors in the application, for example SQL syntax errors, closing a database twice or trying to execute a statement on a closed database.
</li>
<li>
Errors reported from sqlite3 itself, for example a locked database, or an interrupted query and so on.
</li>
</ol>
<p class="text">
In the first case, an error in Lua-Sqlite3 usage, the error is reported with the Lua function error(), which results in terminating the last protected function call.
</p>
<p class="text">
In the second case, when an error during executing a SQL statement occurs, two values are returned: A nil value and an string containing the error message.
</p>
<p class="text">
This behaviour will result in easy catching of all errors using assert() and pcall():
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
function foobar()
assert( some_sqlite3_function() )
assert( another_sqlite3_call() )
assert( a_third_call_to_sqlite3() )
end
ok, errmsg = pcall( foobar )
if not ok then
print("An database error occurred:", errmsg)
end
</pre>
</blockquote>
<h2><a name="ref6">Open and Close </a></h2><blockquote>
<p class="text">
You can open a database that resides in a file on a disc, or you could open a database in memory. As stated above, make sure you use a single point ('.') to call the functions in the 'sqlite3' namespace. Don't use a colon (':') because 'sqlite3' isn't an object:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
-- Open a file database
db = sqlite3.open("filename")
-- Open a temporary database in memory
db = sqlite3.open_memory()
</pre>
<p class="text">
To close a database you invoke the method db:close(). Because this call is a method on the database object, you must use a colon (':') to separate the method name from the object name:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
db:close()
</pre>
<p class="text">
If you close a database, all uncommitted transactions are rolled back, all resources are released automatically. You don't need to close other objects related to this database, but if you would like, you could do so.
</p>
<p class="text">
If closing the database was successfull db:close() returns the database object itself.
</p>
<p class="text">
If there was an error, for example closing the same database twice, an error is raised. When there was an error in sqlite3 itself, a nil and the error message is returned.
</p>
</blockquote>
<h2><a name="ref7">Executing simple SQL Statements</a></h2><blockquote>
<p class="text">
To execute SQL statements which don't return rows, user db:exec() with the SQL statement as an argument:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
db:exec( "CREATE TABLE test (id, data)" )
db:exec[[ INSERT INTO test VALUES (1, "Hello World") ]]
</pre>
<p class="text">
You can also execute multiple SQL statements using a single db:exec() call. The only prerequisite is that none of the statements return rows. You can use statements like CREATE, INSERT, UPDATE, DELETE and so on, but you can't issue a SELECT statement inside a multiple db:exec(). Doing so will raise an error.
</p>
<p class="text">
Multiple SQL statements must be separated using semicolons:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
db:exec[[
BEGIN TRANSACTION;
CREATE TABLE test (id, data);
INSERT INTO test VALUES (1, "Hello World");
INSERT INTO test VALUES (2, "Hello Lua");
INSERT INTO test VALUES (3, "Hello Sqlite3");
END TRANSACTION
]]
</pre>
</blockquote>
<h2><a name="ref8">Fetching Rows using SELECT Statements</a></h2><blockquote>
<p class="text">
To execute a SELECT statement, you can't use db:exec(). Doing so raises an error instead.
</p>
<p class="text">
Rows returned from SELECT statements are fetched using an interator. This works like ipairs() or pairs() in for-loops on tables.
</p>
<p class="text">
You have to choose among three different ways, how the fetched rows should be returned to your loop:
</p>
<ol>
<li>
For each row an array with column data is returned. The columns are indexed with integers.
</li>
<li>
For each row a table with the column data is returned, but the columns are indexed with the column names.
</li>
<li>
Each column data is returned directly.
</li>
</ol>
<p class="text">
An example will make this clear:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
-- Returns a row as an integer indexed array
for row in db:irows("SELECT * FROM test") do
print(row[1], row[2])
end
-- Returns a row as an table, indexed by column names
for row in db:rows("SELECT * FROM test") do
print(row.id, row.data)
end
-- Returns each column directly
for id, data in db:cols("SELECT * FROM test") do
print(id, data)
end
</pre>
<p class="text">
Using db:cols() there exists a subtle caveat: By definition, a for loop in Lua terminates when the first value in the returned values is nil. So you can't use the db:cols() iterator, if your SELECT statement returns rows which first column may contain NULLs (which are converted to NIL). If you do so, immediately before the row that first column contains a NULL, the loop will terminate early.
</p>
<p class="text">
To prevent this behaviour, you could either make sure that the first column in your SELECT statement will never be NULL, for example the first column could always be a row id, or alternatively you could insert a constant and a dummy variable. Example:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
for _, id, data in db:cols("SELECT 1, * FROM test") do
print(id, data)
end
</pre>
<p class="text">
This loop will always return all rows, because the first column is always '1' (the '1' is stored in the dummy variable '_').
</p>
</blockquote>
<h2><a name="ref9">Fetching a Single Row</a></h2><blockquote>
<p class="text">
Often you only need the first row that a SELECT statement returns. For example counting the number of rows in a table and so on. Using db:rows() and related for such a task is a mess, instead you should use db:first_row() and related for this task:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
row = db:first_irow("SELECT count(*) FROM test")
print(row[1])
row = db:first_row("SELECT count(*) AS count FROM test")
print(row.count)
count = db:first_cols("SELECT count(*) FROM test")
print(count)
</pre>
<p class="text">
There doesn't exist a caveat in db:first_cols() like in db:cols(), instead always exactly the columns of the first row from the SELECT statement are returned.
</p>
</blockquote>
<h2><a name="ref10">Simple Prepared Statements</a></h2><blockquote>
<p class="text">
If you have to repeatedly execute the same SQL statement, it will be more efficient if you compile those statements and reuse the compiled form multiple times.
</p>
<p class="text">
When a statement becomes compiled, the statement is parsed and translated to a virtual machine which is stored in a binary representation form in the sqlite3 library itself. This results in a much faster execution of the statement because every time you reuse a compiled statement, the parsing and building of the virtual machine is omitted.
</p>
<p class="text">
Compiling a statement is done with db:prepare(). It will return a compiled statement object:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
stmt = db:prepare("SELECT * FROM test")
</pre>
<p class="text">
To use a compiled statement, it defines the same methods to execute and query a statement like the db object itself. The examples above could be rewritten to:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
stmt = db:prepare("SELECT * FROM test")
for row in stmt:irows() do
print(row[1], row[2])
end
for row in stmt:rows() do
print(row.id, row.data)
end
for id, data in stmt:cols() do
print(id, data)
end
</pre>
<p class="text">
And:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
stmt = db:prepare("SELECT count(*) AS count FROM test")
row = stmt:first_irow()
print(row[1])
row = stmt:first_row()
print(row.count)
count = stmt:first_cols()
print(count)
</pre>
<p class="text">
You could even compile multiple SQL statements:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
stmt = db:prepare[[
INSERT INTO test VALUES (1, "Hello World");
INSERT INTO test VALUES (2, "Hello Lua");
INSERT INTO test VALUES (3, "Hello Sqlite3")
]]
stmt:exec()
</pre>
</blockquote>
<h2><a name="ref11">Prepared Statements with Parameters (Binding)</a></h2><blockquote>
<p class="text">
You can compile SQL statements with placeholder or parameters. So you can reuse the statements later and bind values to the placeholders. With this you can easily reuse parameterized and complicated SQL statements.
</p>
<p class="text">
There are two possibilities to use placeholders or parameters. Mixing the two possibilities in a single compiled statement is not allowed.
</p>
<ol>
<li>
Numbered, anonymous parameters using '?'.
</li>
<li>
Named parameters using ':name' or '$name'.
</li>
</ol>
</blockquote><h3><a name="ref12">Anonymous Parameters</a></h3><blockquote>
<p class="text">
To use a anonymous parameter simply insert a questionmark ('?') in your SQL statement where you would like later bind values to. Then, after you compiled your SQL statement using db:prepare() everytime you would like to bind values to your placeholders, call stmt:bind() to bind the values. The arguments to stmt:bind() are binded to the placeholder in the order the question marks appear in the SQL statement. If the number of arguments to stmt:bind() doesn't match the number of placeholders (questionmarks) in your SQL statement, an error will be raised.
</p>
<p class="text">
You could use parameters and bindings with every valid SQL statement. You can even use placeholders with multiple statements:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
insert_stmt = db:prepare[[
INSERT INTO test VALUES (?, ?);
INSERT INTO test VALUES (?, ?)
]]
function insert(id1, data1, id2, data2)
insert_stmt:bind(id1, data1, id2, data2)
insert_stmt:exec()
end
insert( 1, "Hello World", 2, "Hello Lua" )
insert( 3, "Hello Sqlite3", 4, "Hello User" )
get_stmt = db:prepare("SELECT data FROM test WHERE test.id = ?")
function get_data(id)
get_stmt:bind(id)
return get_stmt:first_cols()
end
print( get_data(1) )
print( get_data(2) )
print( get_data(3) )
print( get_data(4) )
</pre>
</blockquote><h3><a name="ref13">Named Parameters</a></h3><blockquote>
<p class="text">
If you have to bind a lot of values or if you have to use the same value twice or more times in a SQL statement, using the questionmark as a placeholder is error prone because you have to count the questionmarks and make sure you call stmt:bind() with all the values in the right order.
</p>
<p class="text">
Alternatively, you can use named placeholder. A named placeholder is a parameter, which begins with a colon (':') or a dollar sign ('$') followed by alphanumerical characters that build a valid identifier.
</p>
<p class="text">
To define the order of your named placeholder to the stmt:bind() function, you can optionally submit an array with the names of your parameters to db:prepare() as the first argument.
</p>
<p class="text">
The leading colon or dollar sign is optional in the parameter name array. (But of course, the colon or dollar sign in the SQL statement is mandatory.)
</p>
<p class="text">
The ordering of the parameter names in the array determines the order of the arguments to the later stmt:bind() call.
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
db:exec("CREATE TABLE person_name (id, name)")
db:exec("CREATE TABLE person_email (id, email)")
db:exec("CREATE TABLE person_address (id, address)")
-- '$' and ':' are optional
parameter_names = { ":id", "$name", "address", "email" }
stmt = db:prepare(parameter_names, [[
BEGIN TRANSACTION;
INSERT INTO person_name VALUES (:id, :name);
INSERT INTO person_email VALUES (:id, :email);
INSERT INTO person_address VALUES (:id, :address);
COMMIT
]])
function insert(id, name, address, email)
stmt:bind(id, name, address, email)
stmt:exec()
end
insert( 1, "Michael", "Germany", "mroth@nessie.de" )
insert( 2, "John", "USA", "john@usa.org" )
insert( 3, "Hans", "France", "hans@france.com" )
</pre>
</blockquote><h3><a name="ref14">Automatic Parameter Name Mapping</a></h3><blockquote>
<p class="text">
If you don't submit an array containing parameter names to stmt:prepare() the parameter names array becomes automatically build. The parameter names in the automatically build array are ordered according to their first occurrence in the SQL statement.
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
...
stmt = db:prepare[[
BEGIN TRANSACTION;
INSERT INTO person_name VALUES (:id, :name);
INSERT INTO person_email VALUES (:id, :email);
INSERT INTO person_address VALUES (:id, :address);
COMMIT
]]
function insert(id, name, address, email)
-- Please note the different ordering
stmt:bind(id, name, email, address)
stmt:exec()
end
...
</pre>
</blockquote><h3><a name="ref15">Parameter Passing with a Name/Value Table</a></h3><blockquote>
<p class="text">
If you are using named parameters in your statements, you can pass the arguments to stmt:bind() with a name/value table:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
...
stmt = db:prepare[[
BEGIN TRANSACTION;
INSERT INTO person_name VALUES (:id, :name);
INSERT INTO person_email VALUES (:id, :email);
INSERT INTO person_address VALUES (:id, :address);
COMMIT
]]
function insert(id, name, address, email)
args = { }
args.id = id
args.name = name
args.address = address
args.email = args.email
stmt:bind(args)
stmt:exec()
end
-- A shorter version equal to the above
function insert2(id, name, address, email)
stmt:bind{ id=id, name=name, address=address, email=email}
stmt:exec()
end
...
</pre>
</blockquote><h3><a name="ref16">Querying Parameter Names </a></h3><blockquote>
<p class="text">
To query the available parameters in a compiled statement you can use stmt:parameter_names() which returns an array. The parameter names in the returned array have their leading colon or dollar sign stripped:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
...
stmt = db:prepare[[
BEGIN TRANSACTION;
INSERT INTO person_name VALUES (:id, :name);
INSERT INTO person_email VALUES (:id, :email);
INSERT INTO person_address VALUES (:id, :address);
COMMIT
]]
names = stmt:parameter_names()
print( table.getn(names) ) -- "4"
print( names[1] ) -- "id"
print( names[2] ) -- "name"
print( names[3] ) -- "email"
print( names[4] ) -- "address"
...
</pre>
</blockquote>
<h2><a name="ref17">Defining User Functions</a></h2><blockquote>
<p class="text">
You can define user functions in Sqlite3. User defined functions are called from the SQL language interpreted by Sqlite3, back to Lua. You can define functions which calculate complicated things or you can use these user defined functions to call back to Lua from SQL triggers.
</p>
<p class="text">
You need to submit the name of the user function, the number of arguments the user function receives and the function itself:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
function sql_add_ten(a)
return a + 10
end
db:set_function("add_ten", 1, sql_add_ten)
for id, add_ten, data in db::rows("SELECT id, add_ten(id), data FROM test") do
print(id, add_ten, data)
end
</pre>
<p class="text">
You can define functions with variable number of arguments. To do this you have to submit -1 as the number of arguments to db:set_function(). The documentation of sqlite3 states, that any negative number will signal a variable argument function, but at least for sqlite release 3.0.5 this is wrong.
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
function my_max(...)
local result = 0
for _, value in ipairs(arg) do
result = math.max(result, value)
end
return result
end
db:set_function("my_max", -1, my_max)
max1 = db:first_cols("SELECT my_max(17, 7)")
max2 = db:first_cols("SELECT my_max(1, 2, 3, 4, 5)")
print(max1, max2) -- 17 5
</pre>
</blockquote>
<h2><a name="ref18">Defining User Aggregates</a></h2><blockquote>
<p class="text">
A aggregate is a function, which is called multiple times, for each row in the query once, that returns a single value at the end. It is only slightly more complicate than an ordinary function.
</p>
<p class="text">
To define a user aggregate, you have to register a function which returns two functions everytime it is called. The first function is used to update an internal state of the user aggregate and the second function is used to fetch the result from the user aggregate.
</p>
<p class="text">
The exact steps are:
</p>
<ul>
<li>
Step 1: First a function is called, without any arguments, which normally initialize some accumulators of the user aggregate and returns two functions, which are in most cases closures.
</li>
<li>
Step 2: The first function returned in Step 1 is called for every row once. This function is named the 'step' function. The 'step' functions receives the column data as arguments. The 'step' function doesn't return values. It only collects the data and updates some internal state.
</li>
<li>
Step 3: The second function returned in Step 1 is called exactly once, only after all rows were presented to the 'step' function in Step 2 above. This second function is named the 'finalizer' function. The 'finalizer' function receives one argument, the number of how often the 'step' function in Step 2 was called. The 'finalizer' function returns the result of the aggregate.
</li>
</ul>
<p class="text">
An example will make this more clear:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
db:exec[[
CREATE TABLE numbers (num1, num2);
INSERT INTO numbers VALUES(1, 2);
INSERT INTO numbers VALUES(3, 4);
INSERT INTO numbers VALUES(5, 6);
]]
function my_product_sum_aggregate()
local product_sum = 0
local function step(a, b)
local product = a * b
product_sum = product_sum + product
end
local function final(num_called)
return product_sum / num_called
end
return step, final
end
db:set_aggregate("product_sum", 2, my_product_sum_aggregate)
print( db:first_cols("SELECT product_sum(num1, num2) FROM numbers") )
</pre>
<p class="text">
You can define user aggregates with variable number of arguments. In works analogous defining user functions with variable number of arguments.
</p>
</blockquote>
<h2><a name="ref19">Collatores and Collation Handler</a></h2><blockquote>
<p class="text">
Needs to be written...
</p>
</blockquote>
<h2><a name="ref20">Busy Timeout and Handler</a></h2><blockquote>
<p class="text">
In Sqlite3 there are two ways to deal with locked databases. You can set a timeout or set a handler.
</p>
</blockquote><h3><a name="ref21">Busy Timeout</a></h3><blockquote>
<p class="text">
If you set a timeout, Sqlite3 will try as many milliseconds as specified:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
-- Open the database
db = sqlite3.open("filename")
-- Set 2 seconds busy timeout
db:set_busy_timeout(2 * 1000)
-- Use the database
db:exec(...)
</pre>
</blockquote><h3><a name="ref22">Busy Handler</a></h3><blockquote>
<p class="text">
You could also set a handler, which gets called in the most cases if the database is locked. A busy handler could do some other work or wait a few milliseconds. The return value of a busy handler determines if Sqlite3 tries to continue with the current transaction or abort the transaction with a "busy" error.
</p>
<p class="text">
If the busy handler returns 0, false or nil, no additional attempts are made by Sqlite3 to proceed with a transaction. All other values result in a new attempt to be made by Sqlite3.
</p>
<p class="text">
A busy handler gets called with one argument, the number of attempts prior made without success in a transaction.
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
-- Open the database
db = sqlite3.open("filename")
-- Ten attempts are made to proceed, if the database is locked
function my_busy_handler(attempts_made)
if attempts_made &lt; 10 then
return true
else
return false
end
end
-- Set the new busy handler
db:set_busy_handler(my_busy_handler)
-- Use the database
db:exec(...)
</pre>
</blockquote>
<h2><a name="ref23">Progress, Authorizer, Trace and Commit Handler</a></h2><blockquote>
<p class="text">
You guess it, needs to be written...
</p>
<p class="text">
But wait, the trace handler is documented: ;-)
</p>
</blockquote><h3><a name="ref24">Trace Handler</a></h3><blockquote>
<p class="text">
The trace handler in Sqlite3 isn't really a trace handler but it traces effectively compiling SQL statements. So the trace handler is called every time a db:exec() or db:prepare() is executed. The handler gets one argument with the string that was compiled:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
function mytrace(sql_string)
print("Sqlite3:", sql_string)
end
db:set_trace_handler(mytrace)
</pre>
<p class="text">
Note: The tracing behaviour will possible change in future. Then we won't use the internal Sqlite3 trace handler but a self written one which gets called every time a SQL statement is executed.
</p>
</blockquote>
<h2><a name="ref25">Call Chaining</a></h2><blockquote>
<p class="text">
The Sqlite3 Interface makes it possible, to deploy several techniques to write short and efficient code.
</p>
<p class="text">
All methods which doesn't fetch data from the database or creates new objects, return self, like methods in smalltalk.
</p>
<p class="text">
So you can easily build nice chains of method calls:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
sql = "INSERT INTO test VALUES (?, ?)"
id = 5
data = [[That's a matter of taste, if your prefer "'" or '"'.]]
db:prepare(sql):bind(id, data):exec()
</pre>
</blockquote>
<hr size="1">
<h1><a name="ref26">LuaSQL Frontend</a></h1><blockquote>
<p class="text">
There is also a LuaSQL compatible frontend provided. To use the LuaSQL compatible frontend, open it with:
</p>
<pre style="background-color: #eee; border: solid 1px #666; padding: 0.5em;">
require "luasql-sqlite3"
</pre>
<p class="text">
The LuaSQL Frontend doesn't use any utility functions from the LuaSQL framework of the Keppler Project because the framework from the Keppler Project assumes that every LuaSQL compatible database layer is written in C which is not true for this Sqlite3 wrapper. But this detail doesn't matter. You could use the LuaSQL frontend with or without any other LuaSQL Keppler Project packages. The LuaSQL frontend is fully compatible and integrates nicely.
</p>
<p class="text">
For a complete description of the LuaSQL interface, please take a look at <a href="http://www.keplerproject.org/luasql/manual.html">http://www.keplerproject.org/luasql/manual.html</a>.
</p>
<p class="text">
Currently LuaSQL-Sqlite3 implements no extensions, but this will change in the near future too.
</p>
</blockquote>
<hr size="1">
<h1><a name="ref27">libluasqlite3 Wrapper</a></h1><blockquote>
<p class="text">
These parts needs to be written:
</p>
<ol>
<li>
Open Wrapper
</li>
<li>
Return values and Error Handling
</li>
<li>
API Conventions
</li>
<li>
Type Converting
</li>
</ol>
<p class="text">
Please be patient...
</p>
</blockquote>
<hr size="1">
<p><br></p>
<a href="index.html">Back to Lua-Sqlite3</a>
<hr noshade size=1>
<font size=-1>Last update: 2006-05-10<br>
Copyright (c) 2004, 2005, 2006 Michael Roth</font>
</body></html>

View File

@@ -0,0 +1,41 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
assert( db:exec[[
CREATE TABLE test (col1, col2);
INSERT INTO test VALUES (1, 2);
INSERT INTO test VALUES (2, 4);
INSERT INTO test VALUES (3, 6);
INSERT INTO test VALUES (4, 8);
INSERT INTO test VALUES (5, 10);
]] )
assert( db:set_aggregate("my_stats", 2, function()
local square_error_sum = 0
local function step(a, b)
local error = a - b
local square_error = error * error
square_error_sum = square_error_sum + square_error
end
local function final(num_called)
return square_error_sum / num_called
end
return step, final
end))
local my_stats = db:first_cols("SELECT my_stats(col1, col2) FROM test")
print("my_stats:", my_stats)

View File

@@ -0,0 +1,28 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
assert( db:exec[[
CREATE TABLE test (col1, col2);
INSERT INTO test VALUES (1, 2);
INSERT INTO test VALUES (2, 4);
INSERT INTO test VALUES (3, 6);
INSERT INTO test VALUES (4, 8);
INSERT INTO test VALUES (5, 10);
]] )
assert( db:set_function("my_sum", 2, function(a, b)
return a + b
end))
for col1, col2, sum in db:cols("SELECT *, my_sum(col1, col2) FROM test") do
print(col1, col2, sum)
end

View File

@@ -0,0 +1,118 @@
require "path"
require "sqlite3"
local db = assert( sqlite3:open_memory() )
assert( db:exec[[
CREATE TABLE customer (
id INTEGER PRIMARY KEY,
name VARCHAR(40)
);
CREATE TABLE invoice (
id INTEGER PRIMARY KEY,
customer INTEGER NOT NULL,
title VARCHAR(80) NOT NULL,
article1 VARCHAR(40) NOT NULL,
price1 REAL NOT NULL,
article2 VARCHAR(40),
price2 REAL
);
CREATE TABLE invoice_overflow (
id INTEGER PRIMARY KEY,
invoice INTEGER NOT NULL,
article VARCHAR(40) NOT NULL,
price REAL NOT NULL
);
INSERT INTO customer VALUES(
1, "Michael" );
INSERT INTO invoice VALUES(
1, 1, "Computer parts", "harddisc", 89.90, "floppy", 9.99 );
INSERT INTO customer VALUES(
2, "John" );
INSERT INTO invoice VALUES(
2, 2, "Somme food", "apples", 2.79, "pears", 5.99 );
INSERT INTO invoice_overflow VALUES(
NULL, 2, "grapes", 6.34 );
INSERT INTO invoice_overflow VALUES(
NULL, 2, "strawberries", 4.12 );
INSERT INTO invoice_overflow VALUES(
NULL, 2, "tomatoes", 6.17 );
INSERT INTO invoice VALUES(
3, 2, "A new car", "Cybercar XL-1000", 65000.00, NULL, NULL );
]] )
local function customer_name(id)
local stmt = db:prepare("SELECT name FROM customer WHERE id = ?")
return stmt:bind(id):first_cols()
end
local function all_invoices()
return db:rows("SELECT id, customer, title FROM invoice")
end
local function all_articles(invoice)
local function iterator()
local stmt, row
-- Get the articles that are contained in the invoice table itself.
stmt = db:prepare("SELECT article1, price1, article2, price2 FROM invoice WHERE id = ?")
row = stmt:bind(invoice):first_row()
-- Every Invoice has at least one article.
coroutine.yield(row.article1, row.price1)
-- Maybe the Invoice has a second article?
if row.article2 then
-- Yes, there is a second article, so return it.
coroutine.yield(row.article2, row.price2)
-- When there was an second article, maybe there are even
-- more articles in the overflow table? We will see...
stmt = db:prepare("SELECT article, price FROM invoice_overflow WHERE invoice = ? ORDER BY id")
for row in stmt:bind(invoice):rows() do
coroutine.yield(row.article, row.price)
end
end
end
return coroutine.wrap(iterator)
end
for invoice in all_invoices() do
local id = invoice.id
local name = customer_name(invoice.customer)
local title = invoice.title
print()
print("Invoice #"..id..", "..name..": '"..title.."'")
print("----------------------------------------")
for article, price in all_articles(id) do
print( string.format("%20s %8.2f", article, price) )
end
print()
end

View File

@@ -0,0 +1,8 @@
local path = "?;?.lua;../?;../?.lua"
if package == nil then
LUA_PATH = path -- Lua 5.0
else
package.path = path -- Lua 5.1
end

View File

@@ -0,0 +1,18 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
db:exec[[
CREATE TABLE test (id INTEGER PRIMARY KEY, content);
INSERT INTO test VALUES (NULL, 'Hello World');
INSERT INTO test VALUES (NULL, 'Hello Lua');
INSERT INTO test VALUES (NULL, 'Hello Sqlite3')
]]
for row in db:rows("SELECT * FROM test") do
print(row.id, row.content)
end

View File

@@ -0,0 +1,18 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
db:exec[[ CREATE TABLE test (id INTEGER PRIMARY KEY, content) ]]
local stmt = db:prepare[[ INSERT INTO test VALUES (:key, :value) ]]
stmt:bind{ key = 1, value = "Hello World" }:exec()
stmt:bind{ key = 2, value = "Hello Lua" }:exec()
stmt:bind{ key = 3, value = "Hello Sqlite3" }:exec()
for row in db:rows("SELECT * FROM test") do
print(row.id, row.content)
end

View File

@@ -0,0 +1,40 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
db:exec[[
CREATE TABLE test (
id INTEGER PRIMARY KEY,
content VARCHAR
);
]]
local insert_stmt = assert( db:prepare("INSERT INTO test VALUES (NULL, ?)") )
local function insert(data)
insert_stmt:bind(data)
insert_stmt:exec()
end
local select_stmt = assert( db:prepare("SELECT * FROM test") )
local function select()
for row in select_stmt:rows() do
print(row.id, row.content)
end
end
insert("Hello World")
print("First:")
select()
insert("Hello Lua")
print("Second:")
select()
insert("Hello Sqlite3")
print("Third:")
select()

View File

@@ -0,0 +1,21 @@
require "path"
require "sqlite3"
local db = sqlite3.open_memory()
db:set_trace_handler( function(sql)
print("Sqlite Trace:", sql)
end )
db:exec[[
CREATE TABLE test ( id INTEGER PRIMARY KEY, content VARCHAR );
INSERT INTO test VALUES (NULL, 'Hello World');
INSERT INTO test VALUES (NULL, 'Hello Lua');
INSERT INTO test VALUES (NULL, 'Hello Sqlite3');
]]
for row in db:rows("SELECT * FROM test") do
-- NOP
end

View File

@@ -0,0 +1,50 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004, 2006 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
local shared_lib_name = "libluasqlite3.so"
local shared_lib_init = "luaopen_sqlite3"
local shared_lib_path = "SHARED_LIB_PATH"
-- Lua-5.0 / Lua-5.1
local loadlib = loadlib or package.loadlib
local filename = shared_lib_path.."/"..shared_lib_name
local init, error = loadlib(filename, shared_lib_init)
local api, ERR, TYPE, AUTH
if init then
api, ERR, TYPE, AUTH = init()
end
function load_libluasqlite3()
assert(init, error)
return api, ERR, TYPE, AUTH
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
if luasql == nil then
luasql = { }
end
if type(luasql) ~= "table" then
error("luasql needs to be a table, but is a "..type(luasql).." already", 2)
end
local env_class = { }
local conn_class = { }
local cur_class = { }
local env_class_mt = { __index = env_class }
local conn_class_mt = { __index = conn_class }
local cur_class_mt = { __index = cur_class }
local api, ERR, TYPE, AUTH
local function setref(t, v) t.ref_counter = v end
local function ref(t) t.ref_counter = t.ref_counter + 1 end
local function unref(t) t.ref_counter = t.ref_counter - 1 end
local function isref(t) return t.ref_counter > 0 end
function luasql.sqlite3()
if not api then
api, ERR, TYPE, AUTH = load_libluasqlite3()
end
local env = setmetatable( {}, env_class_mt )
setref(env, 0)
env.closed = false
return env
end
function env_class.close(env)
if env.closed or isref(env) then return end
env.closed = true
return true
end
function env_class.connect_memory(env)
return env_class.connect(env, ":memory:")
end
function env_class.connect(env, filename)
if env.closed then return end
local conn = setmetatable( {}, conn_class_mt )
setref(conn, 0)
local err, db = api.open(filename)
if err ~= ERR.OK then error("api.open: "..err) end
conn.env = env
conn.db = db
conn.closed = false
conn.autocommit = true
conn.ta_active = false
ref(env)
return conn
end
function conn_class.close(conn)
if conn.closed or isref(conn) then return end
api.close(conn.db)
conn.db = nil
unref(conn.env)
conn.env = nil
conn.closed = true
return true
end
function conn_class.commit(conn)
if conn.closed or conn.autocommit or not conn.ta_active then return end
api.exec(conn.db, "COMMIT")
conn.ta_active = false
return true
end
function conn_class.rollback(conn)
if conn.closed or conn.autocommit or not conn.ta_active then return end
api.exec(conn.db, "ROLLBACK")
conn.ta_active = false
return true
end
function conn_class.setautocommit(conn, autocommit)
if not conn.autocommit and autocommit then
api.exec("END")
end
conn.autocommit = autocommit
return true
end
function conn_class.execute(conn, sql)
if conn.closed then return end
if not conn.autocommit and not conn.ta_active then
api.exec(conn.db, "BEGIN")
conn.ta_active = true
end
local err, stmt = api.prepare(conn.db, sql)
if err ~= ERR.OK then error("api.prepare: "..err) end
if api.column_count(stmt) == 0 then
-- normal excute, no cursor
err = api.step(stmt)
if err ~= ERR.DONE then
error("api.step: "..err)
end
api.finalize(stmt)
return api.changes(conn.db)
else
-- select, needs cursor
local cur = setmetatable( {}, cur_class_mt )
cur.stmt = stmt
cur.finished = false
cur.closed = false
ref(conn)
ref(conn.env)
cur.conn = conn
return cur
end
end
function cur_class.close(cur)
if cur.closed then return end
if not cur.finished then
api.finalize(cur.stmt)
cur.stmt = nil
end
unref(cur.conn.env)
unref(cur.conn)
cur.conn = nil
cur.closed = true
return true
end
function cur_class.fetch(cur, t, mode)
if cur.closed or cur.finished then return end
-- FIXME: write nice error wrapper...
local err = api.step(cur.stmt)
if err == ERR.DONE then
api.finalize(cur.stmt)
cur.stmt = nil
cur.finished = true
return
elseif err ~= ERR.ROW then
error("api.step: "..err)
end
-- FIXME: really ugly block follows....
if t == nil and mode == nil then
-- direct mode
return api.drow(cur.stmt)
else
if type(t) == "string" and mode == nil then
mode = t
t = { }
end
if mode == nil then
mode = "n"
end
if type(t) ~= "table" or type(mode) ~= "string" then
error("fetch usage error")
end
if mode == nil or mode == "n" then
return api.irow(cur.stmt, t)
elseif mode == "a" then
return api.arow(cur.stmt, t)
else
error("unknown mode: "..mode)
end
end
end
local function get_col_desc(cur, desc, api_func)
if cur.closed then return end
if not cur[desc] then
cur[desc] = { }
for index = 1, api.column_count(cur.stmt) do
table.insert( cur[desc], api_func(cur.stmt, index - 1) )
end
end
return cur[desc]
end
function cur_class.getcolnames(cur)
return get_col_desc(cur, "colnames", api.column_name)
end
function cur_class.getcoltypes(cur)
return get_col_desc(cur, "coltypes", api.column_decltype)
end

693
lua-sqlite3-0.4.1/lunit.lua Normal file
View File

@@ -0,0 +1,693 @@
--[[--------------------------------------------------------------------------
This file is part of lunit 0.4pre (alpha).
For Details about lunit look at: http://www.nessie.de/mroth/lunit/
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
-----------------------
-- Intialize package --
-----------------------
local P = { }
lunit = P
-- Import
local type = type
local print = print
local ipairs = ipairs
local pairs = pairs
local string = string
local table = table
local pcall = pcall
local xpcall = xpcall
local traceback = debug.traceback
local error = error
local setmetatable = setmetatable
local rawset = rawset
local orig_assert = assert
local getfenv = getfenv
local setfenv = setfenv
local tostring = tostring
-- Start package scope
setfenv(1, P)
--------------------------------
-- Private data and functions --
--------------------------------
local run_testcase
local do_assert, check_msg
local stats = { }
local testcases = { }
local stats_inc, tc_mt
--------------------------
-- Type check functions --
--------------------------
function is_nil(x)
return type(x) == "nil"
end
function is_boolean(x)
return type(x) == "boolean"
end
function is_number(x)
return type(x) == "number"
end
function is_string(x)
return type(x) == "string"
end
function is_table(x)
return type(x) == "table"
end
function is_function(x)
return type(x) == "function"
end
function is_thread(x)
return type(x) == "thread"
end
function is_userdata(x)
return type(x) == "userdata"
end
----------------------
-- Assert functions --
----------------------
function assert(assertion, msg)
stats_inc("assertions")
check_msg("assert", msg)
do_assert(not not assertion, "assertion failed (was: "..tostring(assertion)..")", msg) -- (convert assertion to bool)
return assertion
end
function assert_fail(msg)
stats_inc("assertions")
check_msg("assert_fail", msg)
do_assert(false, "failure", msg)
end
function assert_true(actual, msg)
stats_inc("assertions")
check_msg("assert_true", msg)
do_assert(is_boolean(actual), "true expected but was a "..type(actual), msg)
do_assert(actual == true, "true expected but was false", msg)
return actual
end
function assert_false(actual, msg)
stats_inc("assertions")
check_msg("assert_false", msg)
do_assert(is_boolean(actual), "false expected but was a "..type(actual), msg)
do_assert(actual == false, "false expected but was true", msg)
return actual
end
function assert_equal(expected, actual, msg)
stats_inc("assertions")
check_msg("assert_equal", msg)
do_assert(expected == actual, "expected '"..tostring(expected).."' but was '"..tostring(actual).."'", msg)
return actual
end
function assert_not_equal(unexpected, actual, msg)
stats_inc("assertions")
check_msg("assert_not_equal", msg)
do_assert(unexpected ~= actual, "'"..tostring(expected).."' not expected but was one", msg)
return actual
end
function assert_match(pattern, actual, msg)
stats_inc("assertions")
check_msg("assert_match", msg)
do_assert(is_string(pattern), "assert_match expects the pattern as a string")
do_assert(is_string(actual), "expected a string to match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
do_assert(not not string.find(actual, pattern), "expected '"..actual.."' to match pattern '"..pattern.."' but doesn't", msg)
return actual
end
function assert_not_match(pattern, actual, msg)
stats_inc("assertions")
check_msg("assert_not_match", msg)
do_assert(is_string(actual), "expected a string to not match pattern '"..pattern.."' but was a '"..type(actual).."'", msg)
do_assert(string.find(actual, pattern) == nil, "expected '"..actual.."' to not match pattern '"..pattern.."' but it does", msg)
return actual
end
function assert_nil(actual, msg)
stats_inc("assertions")
check_msg("assert_nil", msg)
do_assert(is_nil(actual), "nil expected but was a "..type(actual), msg)
return actual
end
function assert_not_nil(actual, msg)
stats_inc("assertions")
check_msg("assert_not_nil", msg)
do_assert(not is_nil(actual), "nil not expected but was one", msg)
return actual
end
function assert_boolean(actual, msg)
stats_inc("assertions")
check_msg("assert_boolean", msg)
do_assert(is_boolean(actual), "boolean expected but was a "..type(actual), msg)
return actual
end
function assert_not_boolean(actual, msg)
stats_inc("assertions")
check_msg("assert_not_boolean", msg)
do_assert(not is_boolean(actual), "boolean not expected but was one", msg)
return actual
end
function assert_number(actual, msg)
stats_inc("assertions")
check_msg("assert_number", msg)
do_assert(is_number(actual), "number expected but was a "..type(actual), msg)
return actual
end
function assert_not_number(actual, msg)
stats_inc("assertions")
check_msg("assert_not_number", msg)
do_assert(not is_number(actual), "number not expected but was one", msg)
return actual
end
function assert_string(actual, msg)
stats_inc("assertions")
check_msg("assert_string", msg)
do_assert(is_string(actual), "string expected but was a "..type(actual), msg)
return actual
end
function assert_not_string(actual, msg)
stats_inc("assertions")
check_msg("assert_not_string", msg)
do_assert(not is_string(actual), "string not expected but was one", msg)
return actual
end
function assert_table(actual, msg)
stats_inc("assertions")
check_msg("assert_table", msg)
do_assert(is_table(actual), "table expected but was a "..type(actual), msg)
return actual
end
function assert_not_table(actual, msg)
stats_inc("assertions")
check_msg("assert_not_table", msg)
do_assert(not is_table(actual), "table not expected but was one", msg)
return actual
end
function assert_function(actual, msg)
stats_inc("assertions")
check_msg("assert_function", msg)
do_assert(is_function(actual), "function expected but was a "..type(actual), msg)
return actual
end
function assert_not_function(actual, msg)
stats_inc("assertions")
check_msg("assert_not_function", msg)
do_assert(not is_function(actual), "function not expected but was one", msg)
return actual
end
function assert_thread(actual, msg)
stats_inc("assertions")
check_msg("assert_thread", msg)
do_assert(is_thread(actual), "thread expected but was a "..type(actual), msg)
return actual
end
function assert_not_thread(actual, msg)
stats_inc("assertions")
check_msg("assert_not_thread", msg)
do_assert(not is_thread(actual), "thread not expected but was one", msg)
return actual
end
function assert_userdata(actual, msg)
stats_inc("assertions")
check_msg("assert_userdata", msg)
do_assert(is_userdata(actual), "userdata expected but was a "..type(actual), msg)
return actual
end
function assert_not_userdata(actual, msg)
stats_inc("assertions")
check_msg("assert_not_userdata", msg)
do_assert(not is_userdata(actual), "userdata not expected but was one", msg)
return actual
end
function assert_error(msg, func)
stats_inc("assertions")
if is_nil(func) then func, msg = msg, nil end
check_msg("assert_error", msg)
do_assert(is_function(func), "assert_error expects a function as the last argument but it was a "..type(func))
local ok, errmsg = pcall(func)
do_assert(ok == false, "error expected but no error occurred", msg)
end
function assert_pass(msg, func)
stats_inc("assertions")
if is_nil(func) then func, msg = msg, nil end
check_msg("assert_pass", msg)
do_assert(is_function(func), "assert_pass expects a function as the last argument but it was a "..type(func))
local ok, errmsg = pcall(func)
if not ok then do_assert(ok == true, "no error expected but error was: "..errmsg, msg) end
end
-----------------------------------------------------------
-- Assert implementation that assumes it was called from --
-- lunit code which was called directly from user code. --
-----------------------------------------------------------
function do_assert(assertion, base_msg, user_msg)
orig_assert(is_boolean(assertion))
orig_assert(is_string(base_msg))
orig_assert(is_string(user_msg) or is_nil(user_msg))
if not assertion then
if user_msg then
error(base_msg..": "..user_msg, 3)
else
error(base_msg.."!", 3)
end
end
end
-------------------------------------------
-- Checks the msg argument in assert_xxx --
-------------------------------------------
function check_msg(name, msg)
orig_assert(is_string(name))
if not (is_nil(msg) or is_string(msg)) then
error("lunit."..name.."() expects the optional message as a string but it was a "..type(msg).."!" ,3)
end
end
-------------------------------------
-- Creates a new TestCase 'Object' --
-------------------------------------
function TestCase(name)
do_assert(is_string(name), "lunit.TestCase() needs a string as an argument")
local tc = {
__lunit_name = name;
__lunit_setup = nil;
__lunit_tests = { };
__lunit_teardown = nil;
}
setmetatable(tc, tc_mt)
table.insert(testcases, tc)
return tc
end
tc_mt = {
__newindex = function(tc, key, value)
rawset(tc, key, value)
if is_string(key) and is_function(value) then
local name = string.lower(key)
if string.find(name, "^test") or string.find(name, "test$") then
table.insert(tc.__lunit_tests, key)
elseif name == "setup" then
tc.__lunit_setup = value
elseif name == "teardown" then
tc.__lunit_teardown = value
end
end
end
}
-----------------------------------------
-- Wrap Functions in a TestCase object --
-----------------------------------------
function wrap(name, ...)
if is_function(name) then
table.insert(arg, 1, name)
name = "Anonymous Testcase"
end
local tc = TestCase(name)
for index, test in ipairs(arg) do
tc["Test #"..index] = test
end
return tc
end
----------------------------------
-- Runs the complete Test Suite --
----------------------------------
function run()
---------------------------
-- Initialize statistics --
---------------------------
stats.testcases = 0 -- Total number of Test Cases
stats.tests = 0 -- Total number of all Tests in all Test Cases
stats.run = 0 -- Number of Tests run
stats.notrun = 0 -- Number of Tests not run
stats.failed = 0 -- Number of Tests failed
stats.warnings = 0 -- Number of Warnings (teardown)
stats.errors = 0 -- Number of Errors (setup)
stats.passed = 0 -- Number of Test passed
stats.assertions = 0 -- Number of all assertions made in all Test in all Test Cases
--------------------------------
-- Count Test Cases and Tests --
--------------------------------
stats.testcases = table.getn(testcases)
for _, tc in ipairs(testcases) do
stats_inc("tests" , table.getn(tc.__lunit_tests))
end
------------------
-- Print Header --
------------------
print()
print("#### Test Suite with "..stats.tests.." Tests in "..stats.testcases.." Test Cases loaded.")
------------------------
-- Run all Test Cases --
------------------------
for _, tc in ipairs(testcases) do
run_testcase(tc)
end
------------------
-- Print Footer --
------------------
print()
print("#### Test Suite finished.")
local msg_assertions = stats.assertions.." Assertions checked. "
local msg_passed = stats.passed == stats.tests and "All Tests passed" or stats.passed.." Tests passed"
local msg_failed = stats.failed > 0 and ", "..stats.failed.." failed" or ""
local msg_run = stats.notrun > 0 and ", "..stats.notrun.." not run" or ""
local msg_warn = stats.warnings > 0 and ", "..stats.warnings.." warnings" or ""
print()
print(msg_assertions..msg_passed..msg_failed..msg_run..msg_warn.."!")
-----------------
-- Return code --
-----------------
if stats.passed == stats.tests then
return 0
else
return 1
end
end
-----------------------------
-- Runs a single Test Case --
-----------------------------
function run_testcase(tc)
orig_assert(is_table(tc))
orig_assert(is_table(tc.__lunit_tests))
orig_assert(is_string(tc.__lunit_name))
orig_assert(is_nil(tc.__lunit_setup) or is_function(tc.__lunit_setup))
orig_assert(is_nil(tc.__lunit_teardown) or is_function(tc.__lunit_teardown))
----------------------------------
-- Protected call to a function --
----------------------------------
local function call(errprefix, func)
orig_assert(is_string(errprefix))
orig_assert(is_function(func))
local ok, errmsg = xpcall(function() func(tc) end, traceback)
if not ok then
print()
print(errprefix..": "..errmsg)
end
return ok
end
------------------------------------
-- Calls setup() on the Test Case --
------------------------------------
local function setup(testname)
if tc.__lunit_setup then
return call("ERROR: "..testname..": setup() failed", tc.__lunit_setup)
else
return true
end
end
------------------------------------------
-- Calls a single Test on the Test Case --
------------------------------------------
local function run(testname)
orig_assert(is_string(testname))
orig_assert(is_function(tc[testname]))
local ok = call("FAIL: "..testname, tc[testname])
if not ok then
stats_inc("failed")
else
stats_inc("passed")
end
return ok
end
---------------------------------------
-- Calls teardown() on the Test Case --
---------------------------------------
local function teardown(testname)
if tc.__lunit_teardown then
if not call("WARNING: "..testname..": teardown() failed", tc.__lunit_teardown) then
stats_inc("warnings")
end
end
end
---------------------------------
-- Run all Tests on a TestCase --
---------------------------------
print()
print("#### Running '"..tc.__lunit_name.."' ("..table.getn(tc.__lunit_tests).." Tests)...")
for _, testname in ipairs(tc.__lunit_tests) do
if setup(testname) then
run(testname)
stats_inc("run")
teardown(testname)
else
print("WARN: Skipping '"..testname.."'...")
stats_inc("notrun")
end
end
end
---------------------
-- Import function --
---------------------
function import(name)
do_assert(is_string(name), "lunit.import() expects a single string as argument")
local user_env = getfenv(2)
--------------------------------------------------
-- Installs a specific function in the user env --
--------------------------------------------------
local function install(funcname)
user_env[funcname] = P[funcname]
end
----------------------------------------------------------
-- Install functions matching a pattern in the user env --
----------------------------------------------------------
local function install_pattern(pattern)
for funcname, _ in pairs(P) do
if string.find(funcname, pattern) then
install(funcname)
end
end
end
------------------------------------------------------------
-- Installs assert() and all assert_xxx() in the user env --
------------------------------------------------------------
local function install_asserts()
install_pattern("^assert.*")
end
-------------------------------------------
-- Installs all is_xxx() in the user env --
-------------------------------------------
local function install_tests()
install_pattern("^is_.+")
end
if name == "asserts" or name == "assertions" then
install_asserts()
elseif name == "tests" or name == "checks" then
install_tests()
elseif name == "all" then
install_asserts()
install_tests()
install("TestCase")
elseif string.find(name, "^assert.*") and P[name] then
install(name)
elseif string.find(name, "^is_.+") and P[name] then
install(name)
elseif name == "TestCase" then
install("TestCase")
else
error("luniit.import(): invalid function '"..name.."' to import", 2)
end
end
--------------------------------------------------
-- Installs a private environment on the caller --
--------------------------------------------------
function setprivfenv()
local new_env = { }
local new_env_mt = { __index = getfenv(2) }
setmetatable(new_env, new_env_mt)
setfenv(2, new_env)
end
--------------------------------------------------
-- Increments a counter in the statistics table --
--------------------------------------------------
function stats_inc(varname, value)
orig_assert(is_table(stats))
orig_assert(is_string(varname))
orig_assert(is_nil(value) or is_number(value))
if not stats[varname] then return end
stats[varname] = stats[varname] + (value or 1)
end

View File

@@ -0,0 +1,843 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004, 2005 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
--[[
TODO:
collation set_collation(name, func)
collation_needed set_collation_handler(func)
progress_handler set_progress_handler(func)
authorizer set_auth_handler(func)
commit_hook set_commit_handler(func)
--]]
require "libluasqlite3-loader"
local api, ERR, TYPE, AUTH = load_libluasqlite3()
local db_class = { }
local stmt_class = { }
local function check_stmt(stmt)
assert(type(stmt.handles) == "table", "Prepared statement expected")
return stmt
end
local function check_single_stmt(stmt)
assert(type(stmt.handles) == "table" and table.getn(stmt.handles) == 1, "Single prepared statement expected")
return stmt.handles[1]
end
local function check_db(db)
assert(db.handle, "Open database handle expected")
return db
end
local function check_table(tab, msg)
assert(type(tab) == "table", msg)
return tab
end
local function check_string(str, msg)
assert( type(str) == "string", msg )
return str
end
local function check_number(num, msg)
assert( type(num) == "number", msg )
return num
end
-----------------------------------------------------
-- Error test und report helper for sqlite3 errors --
-----------------------------------------------------
local function is_error(status)
return not ( status == ERR.OK or status == ERR.ROW or status == ERR.DONE )
end
local function is_row(status)
return status == ERR.ROW
end
local function is_done(status)
return status == ERR.DONE
end
local function errmsg(db_handle)
return api.errmsg(db_handle) or "Unknown error"
end
-------------------------------------------------------------------------
-- Creates an oject. An object is a table with itself set as metatable --
-------------------------------------------------------------------------
local function object()
local t = { }
setmetatable(t, t)
return t
end
-----------------------------------
-- Registry for tables (objects) --
-----------------------------------
local function create_registry()
return { 0 }
end
local function register(registry, object)
local id = registry[1]
if id == 0 then
table.insert(registry, object)
return table.getn(registry)
else
registry[1] = registry[id]
registry[id] = object
return id
end
end
local function unregister(registry, id)
registry[id] = registry[1]
registry[1] = id
end
-------------
-- sqlite3 --
-------------
sqlite3 = { }
function sqlite3.open(filename)
check_string(filename, "Filename as string expected")
local status, handle = api.open(filename)
if is_error(status) then
local errmsg = errmsg(handle)
api.close(handle)
return nil, errmsg
end
local db = object()
db.__gc = db_class.close
db.__index = db_class
db.filename = filename
db.handle = handle
db.stmts = create_registry()
return db
end
function sqlite3.open_memory()
return sqlite3.open(":memory:")
end
--------------------
-- Database Class --
--------------------
function db_class.close(db)
check_db(db)
for _, obj in ipairs(db.stmts) do
if type(obj) == "table" then
obj:close()
end
end
local status = api.close(db.handle)
if is_error(status) then
return nil, errmsg(db.handle)
end
db.handle = nil
db.stmts = nil
db.__gc = nil
return db
end
function db_class.interrupt(db)
check_db(db)
local status = api.interrupt(db.handle)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.last_insert_rowid(db)
check_db(db)
return api.last_insert_rowid(db.handle)
end
function db_class.changes(db)
check_db(db)
return api.changes(db.handle)
end
function db_class.total_changes(db)
check_db(db)
return api.total_changes(db.handle)
end
function db_class.exec(db, sql)
check_db(db)
check_string(sql)
local status = api.exec(db.handle, sql)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.irows(db, sql, tab)
check_db(db)
return db:prepare(sql):irows(tab, true)
end
function db_class.rows(db, sql, tab)
check_db(db)
return db:prepare(sql):rows(tab, true)
end
function db_class.cols(db, sql)
check_db(db)
return db:prepare(sql):cols(true)
end
function db_class.first_irow(db, sql, tab)
check_db(db)
return db:prepare(sql):first_irow(tab, true)
end
function db_class.first_row(db, sql, tab)
check_db(db)
return db:prepare(sql):first_row(tab, true)
end
function db_class.first_cols(db, sql)
check_db(db)
return db:prepare(sql):first_cols(true)
end
function db_class.prepare(db, paranames, sql)
check_db(db)
if sql == nil then
sql = paranames
paranames = nil
end
check_string(sql, "db:prepare: SQL statement as string expected")
local function cleanup(handles)
for _, handle in ipairs(handles) do
api.finalize(handle)
end
end
local function count_parameters(handles)
local parameter_count = 0
for _, handle in ipairs(handles) do
parameter_count = parameter_count + api.bind_parameter_count(handle)
end
return parameter_count
end
local function build_handles(sql)
local status, handle
local remaining = sql
local handles = { }
while remaining do
status, handle, remaining = api.prepare(db.handle, remaining)
if is_error(status) then
local errmsg = errmsg(db.handle)
cleanup(handles)
return nil, errmsg
end
table.insert(handles, handle)
end
return handles
end
local function anonymous_parameters(handles)
for _, handle in ipairs(handles) do
for i = 1, api.bind_parameter_count(handle) do
if api.bind_parameter_name_x(handle, i) then
return false
end
end
end
return true
end
local function named_parameters(handles)
for _, handle in ipairs(handles) do
for i = 1, api.bind_parameter_count(handle) do
if not api.bind_parameter_name_x(handle, i) then
return false
end
end
end
return true
end
local function create_mapping(handles, paranames)
local invers = { }
for index, name in ipairs(paranames) do
invers[name] = index
end
local mapping = { }
for _, handle in ipairs(handles) do
for index = 1, api.bind_parameter_count(handle) do
local parameter_name = api.bind_parameter_name_x(handle, index)
local pos = invers[parameter_name]
if pos == nil then
cleanup(handles)
return nil, "db:prepare: Unknown parameter name '" .. parameter_name .. "'in statement."
end
table.insert(mapping, pos)
end
end
return mapping
end
local function collect_parameter_names(handles)
local seen = { }
local names = { }
for _, handle in ipairs(handles) do
for index = 1, api.bind_parameter_count(handle) do
local parameter_name = api.bind_parameter_name_x(handle, index)
if not seen[parameter_name] then
table.insert(names, parameter_name)
seen[parameter_name] = true
end
end
end
return names
end
local function fix_parameter_names(unfixed_parameters)
local fixed_parameters = { }
for _, unfixed_name in ipairs(unfixed_parameters) do
local _, _, fixed_name = string.find(unfixed_name, "^[:$]?(%a%w*)$")
if not fixed_name then
return nil, "db:prepare: Invalid parameter name: '" .. unfixed_name .."'."
end
table.insert(fixed_parameters, fixed_name)
end
return fixed_parameters
end
local function create_stmt(db, handles, parameter_count)
local stmt = object()
stmt.__gc = stmt_class.close
stmt.__index = stmt_class
stmt.handles = handles
stmt.db = db
stmt.reg_id = register(db.stmts, stmt)
stmt.parameter_count= parameter_count
return stmt
end
local handles, errmsg = build_handles(sql)
if errmsg then
return nil, errmsg
end
local parameter_count = count_parameters(handles)
if parameter_count == 0 then -- No parameters at all
return create_stmt(db, handles, 0)
else
if anonymous_parameters(handles) then -- All parameters are anonymous ("?")
return create_stmt(db, handles, parameter_count)
elseif named_parameters(handles) then -- All parameters are named (":foobar" & "$foobar")
if paranames then -- Fixed mapping of parameter names
check_table(paranames, "db:prepare: Names of parameters expected as strings")
local fixed_parameter_names, errmsg = fix_parameter_names(paranames)
if errmsg then
cleanup(handles)
return nil, errmgs
end
local mapping, errmsg = create_mapping(handles, fixed_parameter_names)
if errmsg then
cleanup(handles)
return nil, errmsg
end
local stmt = create_stmt(db, handles, table.getn(fixed_parameter_names))
stmt.mapping = mapping
stmt.paranames = fixed_parameter_names
return stmt
else -- Automatic mapping of paramter names
local parameter_names = collect_parameter_names(handles)
local mapping = create_mapping(handles, parameter_names)
local stmt = create_stmt(db, handles, table.getn(parameter_names))
stmt.mapping = mapping
stmt.paranames = parameter_names
return stmt
end
else -- Mixed paramters are not allowed
cleanup(handles)
return nil, "db:prepare: Mixed anonymous and named parameters are not allowed."
end
end
end
local function call_user_func(context, func, num_values, values)
-- Don't use table.insert() because of nils in lua-5.1
local arg = { }
for index = 1, num_values do
arg[index] = api.value(values, index-1)
end
-- Make lua-5.0.2 unpack() happy
arg.n = num_values
-- lua-5.1 unpack() style / lua-5.0.2 ignores additional arguments
local ok, result = pcall(func, unpack(arg, 1, num_values))
if not ok then
api.result_error(context, tostring(result))
else
api.result(context, result)
end
end
function db_class.set_function(db, name, num_args, func)
check_db(db)
local function xfunc(context, num_values, values)
call_user_func(context, func, num_values, values)
end
local status = api.create_function(db.handle, name, num_args, xfunc, nil, nil)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_aggregate(db, name, num_args, create_funcs)
check_db(db)
local step, final
local function xstep(context, num_values, values)
if not step and not final then
step, final = create_funcs()
end
call_user_func(context, step, num_values, values)
end
local function xfinal(context)
local ok, result = pcall(final, api.aggregate_count(context))
if not ok then
api.result_error(context, tostring(result))
else
api.result(context, result)
end
end
local status = api.create_function(db.handle, name, num_args, nil, xstep, xfinal)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_trace_handler(db, func)
check_db(db)
local status = api.trace(db.handle, func)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_busy_timeout(db, ms)
check_db(db)
local status = api.busy_timeout(db.handle, ms)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
function db_class.set_busy_handler(db, func)
check_db(db)
local status = api.busy_handler(db.handle, func)
if is_error(status) then
return nil, errmsg(db.handle)
end
return db
end
---------------------
-- Statement Class --
---------------------
function stmt_class.bind(stmt, ...)
local function bind_with_mapping(parameters)
local mapping = stmt.mapping
local map_index = 1
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[mapping[map_index]])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
map_index = map_index + 1
end
end
end
local function bind_without_mapping(parameters)
local parameter_index = 1
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[parameter_index])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
parameter_index = parameter_index + 1
end
end
end
local function bind_by_names(parameters)
local parameter_names = stmt.paranames
local mapping = stmt.mapping
for _, handle in ipairs(stmt.handles) do
for index = 1, api.bind_parameter_count(handle) do
local status = api.bind(handle, index, parameters[parameter_names[mapping[index]]])
if is_error(status) then
return nil, errmsg(stmt.db.handle)
end
end
end
end
check_stmt(stmt)
if stmt.parameter_count == 0 then error("stmt:bind: statement contains no parameters.") end
if type(arg[1]) == "table" and arg.n == 1 and stmt.mapping and stmt.paranames then
bind_by_names(arg[1])
else
if arg.n < stmt.parameter_count then error("stmt:bind: to few parameters.") end
if arg.n > stmt.parameter_count then error("stmt:bind: to many parameters.") end
if stmt.mapping then
bind_with_mapping(arg)
else
bind_without_mapping(arg)
end
end
return stmt
end
function stmt_class.reset(stmt)
check_stmt(stmt)
for _, handle in ipairs(stmt.handles) do
api.reset(handle)
end
return stmt
end
function stmt_class.close(stmt)
check_stmt(stmt)
for _, handle in ipairs(stmt.handles) do
api.finalize(handle)
end
unregister(stmt.db.stmts, stmt.reg_id)
stmt.db = nil
stmt.handles = nil
stmt.mapping = nil
stmt.__gc = nil
return stmt
end
local no_parameter_names = { }
function stmt_class.parameter_names(stmt)
check_stmt(stmt)
if not stmt.paranames then
return no_parameter_names
else
return stmt.paranames
end
end
local function stmt_column_info(stmt, info_func)
local handle = check_single_stmt(stmt)
local info = { }
for index = 1, api.column_count(handle) do
table.insert(info, info_func(handle, index-1) )
end
return info
end
function stmt_class.column_names(stmt)
return stmt_column_info(stmt, api.column_name)
end
function stmt_class.column_decltypes(stmt)
return stmt_column_info(stmt, api.column_decltype)
end
function stmt_class.column_count(stmt)
local handle = check_single_stmt(stmt)
return api.column_count(handle)
end
function stmt_class.exec(stmt)
check_stmt(stmt)
stmt:reset()
for _, handle in ipairs(stmt.handles) do
while true do
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
api.reset(handle)
return nil, errmsg
end
if is_done(status) then
break
end
end
api.reset(handle)
end
return stmt
end
local function stmt_rows(stmt, get_row_func, tab, autoclose)
local handle = check_single_stmt(stmt)
api.reset(handle)
local function check_autoclose()
if autoclose == true then
stmt:close()
else
api.reset(handle)
end
end
local iterator = function()
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
check_autoclose()
return nil, errmsg
end
if is_row(status) then
return get_row_func(handle, tab)
end
if is_done(status) then
check_autoclose()
return nil
end
return nil, "stmt:rows: Internal error!"
end
return iterator
end
function stmt_class.irows(stmt, tab, autoclose)
return stmt_rows(stmt, api.irow, tab, autoclose)
end
function stmt_class.rows(stmt, tab, autoclose)
return stmt_rows(stmt, api.arow, tab, autoclose)
end
function stmt_class.cols(stmt, autoclose)
return stmt_rows(stmt, api.drow, nil, autoclose)
end
local function first_row(stmt, get_row_func, tab, autoclose)
local handle = check_single_stmt(stmt)
api.reset(handle)
local function check_autoclose()
if autoclose == true then
stmt:close()
else
api.reset(handle)
end
end
local status = api.step(handle)
if is_error(status) then
local errmsg = errmsg(stmt.db.handle)
check_autoclose()
return nil, errmsg
end
if is_row(status) then
local row = get_row_func(handle, tab)
check_autoclose()
return row
end
if is_done(status) then
check_autoclose()
return nil, "No row returned."
end
return nil, "stmt:first_row: Internal error!"
end
function stmt_class.first_irow(stmt, tab, autoclose)
return first_row(stmt, api.irow, tab, autoclose)
end
function stmt_class.first_row(stmt, tab, autoclose)
return first_row(stmt, api.arow, tab, autoclose)
end
function stmt_class.first_cols(stmt, autoclose)
local count = api.column_count(stmt.handles[1])
local row, errmsg = first_row(stmt, api.irow, nil, autoclose)
if errmsg then
return nil, errmsg
else
row.n = count -- Make lua-5.0.2 unpack() happy
return unpack(row, 1, count) -- lua-5.1 style / lua-5.0.2 ignores additional arguments
end
end

View File

@@ -0,0 +1,241 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
-------------------------------------------------
-- This is the luaSql compatible sqlite driver --
-------------------------------------------------
require "luasql-sqlite3"
require "lunit"
lunit.setprivfenv()
lunit.import "asserts"
lunit.wrap("Driver Interface", function()
assert_table( luasql )
assert_function( luasql.sqlite3 )
local env = assert_table( luasql.sqlite3() )
assert_function( env.connect )
assert_function( env.connect_memory ) -- sqlite3 extension
assert_function( env.close )
assert_true( env:close(), "Closing an unused environment must return 'true'" )
assert_nil( env:close(), "Closing a closed environment must return 'nil'" )
end)
lunit.wrap("Connection Interface", function()
local env = assert_table( luasql.sqlite3() )
local con = assert_table( env:connect_memory() )
assert_function( con.close )
assert_function( con.execute )
assert_function( con.rollback )
assert_function( con.commit )
assert_function( con.setautocommit )
assert_true( con:close(), "Closing an open connection must return 'true'" )
assert_nil( con:close(), "Closing a closed connection must return 'nil'" )
assert_true( env:close() )
end)
lunit.wrap("Simple connection usage", function()
local env = assert_table( luasql.sqlite3() )
local con = assert_table( env:connect_memory() )
assert_equal( 0, con:execute("CREATE TABLE test (id, name)") )
assert_equal( 1, con:execute("INSERT INTO test VALUES (1, 'Hello World')") )
assert_true( con:close() )
assert_true( env:close() )
end)
lunit.wrap("Cursor Interface", function()
local env = assert_table( luasql.sqlite3() )
local con = assert_table( env:connect_memory() )
assert_equal( 0, con:execute("CREATE TABLE test (id, name)") )
local cur = assert_table( con:execute("SELECT * FROM test") )
assert_function( cur.close )
assert_function( cur.fetch )
assert_function( cur.getcolnames )
assert_function( cur.getcoltypes )
assert_true( cur:close(), "Closing an open cursor must return 'true'" )
assert_nil( cur:close(), "Closing a closed cursor must return 'nil'" )
assert_true( con:close() )
assert_true( env:close() )
end)
local scu = lunit.TestCase("Simple cursor usage")
function scu:setup()
-- open database
self.env = assert_table( luasql.sqlite3() )
self.con = assert_table( self.env:connect_memory() )
-- prepare database
assert_equal( 0, self.con:execute("CREATE TABLE test (id INTEGER, item TEXT)") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (1, 'Hello World')") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (2, 'Hello Lua')") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (3, 'Hello sqlite3')") )
-- open cursor
self.cur = assert_table( self.con:execute("SELECT * FROM test ORDER BY id") )
end
function scu:teardown()
assert_true( self.cur:close() )
assert_true( self.con:close() )
assert_true( self.env:close() )
end
function scu:test_fetch_direct()
local id, item
id, item = self.cur:fetch(); assert_equal(1, id); assert_equal("Hello World", item)
id, item = self.cur:fetch(); assert_equal(2, id); assert_equal("Hello Lua", item)
id, item = self.cur:fetch(); assert_equal(3, id); assert_equal("Hello sqlite3", item)
assert_nil( self.cur:fetch() )
end
function scu.check(key_id, id, key_item, item, row)
assert_table(row)
assert_equal(id, row[key_id])
assert_equal(item, row[key_item])
end
function scu:test_fetch_default()
self.check(1, 1, 2, "Hello World", self.cur:fetch({}) )
self.check(1, 2, 2, "Hello Lua", self.cur:fetch({}) )
self.check(1, 3, 2, "Hello sqlite3", self.cur:fetch({}) )
assert_nil( self.cur:fetch({}) )
end
function scu:test_fetch_numeric()
self.check(1, 1, 2, "Hello World", self.cur:fetch({}, "n") )
self.check(1, 2, 2, "Hello Lua", self.cur:fetch({}, "n") )
self.check(1, 3, 2, "Hello sqlite3", self.cur:fetch({}, "n") )
assert_nil( self.cur:fetch({}, "n") )
end
function scu:test_fetch_alphanumeric()
self.check("id", 1, "item", "Hello World", self.cur:fetch({}, "a"))
self.check("id", 2, "item", "Hello Lua", self.cur:fetch({}, "a"))
self.check("id", 3, "item", "Hello sqlite3", self.cur:fetch({}, "a"))
assert_nil( self.cur:fetch({}, "a") )
end
function scu:test_getcolnames()
local names = assert_table( self.cur:getcolnames() )
assert_equal(2, table.getn(names) )
assert_equal("id", names[1])
assert_equal("item", names[2])
end
function scu:test_getcoltypes()
local types = assert_table( self.cur:getcoltypes() )
assert_equal(2, table.getn(types) )
assert_equal("INTEGER", types[1])
assert_equal("TEXT", types[2])
end
local ta = lunit.TestCase("Transaction Tests")
function ta:setup()
-- open database
self.env = assert_table( luasql.sqlite3() )
self.con = assert_table( self.env:connect_memory() )
-- prepare database
assert_equal( 0, self.con:execute("CREATE TABLE test (id INTEGER, item TEXT)") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (1, 'Hello World')") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (2, 'Hello Lua')") )
assert_equal( 1, self.con:execute("INSERT INTO test VALUES (3, 'Hello sqlite3')") )
-- switch to manual transaction controll
assert_true( self.con:setautocommit(false) )
end
function ta:teardown()
assert_true( self.con:close() )
assert_true( self.env:close() )
end
function ta:insert(id, item)
assert_equal(1, self.con:execute("INSERT INTO test VALUES ("..id..", '"..item.."')") )
end
function ta:update(id, item)
assert_equal(1, self.con:execute("UPDATE test SET item = '"..item.."' WHERE id = "..id) )
end
function ta:check(expected)
assert_table(expected)
local cur = assert_table( self.con:execute("SELECT * FROM test ORDER BY id") )
local id = 0
local row = cur:fetch({}, "a")
while row do
assert_table(row)
id = id + 1
assert_equal(id, row.id, "Unexpected 'id' read (wrong row?)")
assert( id <= table.getn(expected), "'Id' read to large (to many rows?)")
assert_equal(expected[id], row.item, "Invalid content in row")
row = cur:fetch({}, "a")
end
assert_equal(id, table.getn(expected), "To less rows read")
assert_true( cur:close() )
end
function ta:test_prepared_content()
self:check { "Hello World", "Hello Lua", "Hello sqlite3" }
end
function ta:test_transactions()
self:insert(4, "Hello again")
self:insert(5, "Goodbye")
self:check { "Hello World", "Hello Lua", "Hello sqlite3", "Hello again", "Goodbye" }
assert_true( self.con:commit() )
self:update(1, "Good morning")
self:insert(6, "Foobar")
self:check { "Good morning", "Hello Lua", "Hello sqlite3", "Hello again", "Goodbye", "Foobar" }
assert_true( self.con:rollback() )
self:check { "Hello World", "Hello Lua", "Hello sqlite3", "Hello again", "Goodbye" }
end

View File

@@ -0,0 +1,472 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004, 2005 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
require "sqlite3"
require "lunit"
lunit.setprivfenv()
lunit.import "assertions"
lunit.import "checks"
-------------------------------
-- Basic open and close test --
-------------------------------
lunit.wrap("open_memory", function()
local db = assert_table( sqlite3.open_memory() )
assert( db:close() )
end)
lunit.wrap("open", function()
local filename = "/tmp/__lua-sqlite3-20040906135849." .. os.time()
local db = assert_table( sqlite3.open(filename) )
assert( db:close() )
os.remove(filename)
end)
-------------------------------------
-- Presence of db member functions --
-------------------------------------
local db_funcs = lunit.TestCase("Database Member Functions")
function db_funcs:setup()
self.db = assert( sqlite3.open_memory() )
end
function db_funcs:teardown()
assert( self.db:close() )
end
function db_funcs:test()
local db = self.db
assert_function( db.close )
assert_function( db.exec )
assert_function( db.irows )
assert_function( db.rows )
assert_function( db.cols )
assert_function( db.first_irow )
assert_function( db.first_row )
assert_function( db.first_cols )
assert_function( db.prepare )
assert_function( db.interrupt )
assert_function( db.last_insert_rowid )
assert_function( db.changes )
assert_function( db.total_changes )
end
---------------------------------------
-- Presence of stmt member functions --
---------------------------------------
local stmt_funcs = lunit.TestCase("Statement Member Functions")
function stmt_funcs:setup()
self.db = assert( sqlite3.open_memory() )
self.stmt = assert( self.db:prepare("CREATE TABLE test (id, content)") )
end
function stmt_funcs:teardown()
assert( self.stmt:close() )
assert( self.db:close() )
end
function stmt_funcs:test()
local stmt = self.stmt
assert_function( stmt.close )
assert_function( stmt.reset )
assert_function( stmt.exec )
assert_function( stmt.bind )
assert_function( stmt.irows )
assert_function( stmt.rows )
assert_function( stmt.cols )
assert_function( stmt.first_irow )
assert_function( stmt.first_row )
assert_function( stmt.first_cols )
assert_function( stmt.column_names )
assert_function( stmt.column_decltypes )
assert_function( stmt.column_count )
end
------------------
-- Tests basics --
------------------
local basics = lunit.TestCase("Basics")
function basics:setup()
self.db = assert_table( sqlite3.open_memory() )
end
function basics:teardown()
assert_table( self.db:close() )
end
function basics:create_table()
assert_table( self.db:exec("CREATE TABLE test (id, name)") )
end
function basics:drop_table()
assert_table( self.db:exec("DROP TABLE test") )
end
function basics:insert(id, name)
assert_table( self.db:exec("INSERT INTO test VALUES ("..id..", '"..name.."')") )
end
function basics:update(id, name)
assert_table( self.db:exec("UPDATE test SET name = '"..name.."' WHERE id = "..id) )
end
function basics:test_create_drop()
self:create_table()
self:drop_table()
end
function basics:test_multi_create_drop()
self:create_table()
self:drop_table()
self:create_table()
self:drop_table()
end
function basics:test_insert()
self:create_table()
self:insert(1, "Hello World")
self:insert(2, "Hello Lua")
self:insert(3, "Hello sqlite3")
end
function basics:test_update()
self:create_table()
self:insert(1, "Hello Home")
self:insert(2, "Hello Lua")
self:update(1, "Hello World")
end
---------------------------------
-- Statement Column Info Tests --
---------------------------------
lunit.wrap("Column Info Test", function()
local db = assert_table( sqlite3.open_memory() )
assert_table( db:exec("CREATE TABLE test (id INTEGER, name TEXT)") )
local stmt = assert_table( db:prepare("SELECT * FROM test") )
assert_equal(2, stmt:column_count(), "Wrong number of columns." )
local names = assert_table( stmt:column_names() )
assert_equal(2, table.getn(names), "Wrong number of names.")
assert_equal("id", names[1] )
assert_equal("name", names[2] )
local types = assert_table( stmt:column_decltypes() )
assert_equal(2, table.getn(types), "Wrong number of declaration types.")
assert_equal("INTEGER", types[1] )
assert_equal("TEXT", types[2] )
assert_table( stmt:close() )
assert_table( db:close() )
end)
---------------------
-- Statement Tests --
---------------------
st = lunit.TestCase("Statement Tests")
function st:setup()
self.db = assert( sqlite3.open_memory() )
assert_table( self.db:exec("CREATE TABLE test (id, name)") )
assert_table( self.db:exec("INSERT INTO test VALUES (1, 'Hello World')") )
assert_table( self.db:exec("INSERT INTO test VALUES (2, 'Hello Lua')") )
assert_table( self.db:exec("INSERT INTO test VALUES (3, 'Hello sqlite3')") )
end
function st:teardown()
assert_table( self.db:close() )
end
function st:check_content(expected)
local stmt = assert( self.db:prepare("SELECT * FROM test ORDER BY id") )
local i = 0
for row in stmt:irows() do
i = i + 1
assert( i <= table.getn(expected), "To much rows." )
assert_equal(2, table.getn(row), "Two result column expected.")
assert_equal(i, row[1], "Wrong 'id'.")
assert_equal(expected[i], row[2], "Wrong 'name'.")
end
assert_equal( table.getn(expected), i, "To few rows." )
assert_table( stmt:close() )
end
function st:test_setup()
assert_pass(function() self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3" } end)
assert_error(function() self:check_content{ "Hello World", "Hello Lua" } end)
assert_error(function() self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "To much" } end)
assert_error(function() self:check_content{ "Hello World", "Hello Lua", "Wrong" } end)
assert_error(function() self:check_content{ "Hello World", "Wrong", "Hello sqlite3" } end)
assert_error(function() self:check_content{ "Wrong", "Hello Lua", "Hello sqlite3" } end)
end
function st:test_questionmark_args()
local stmt = assert_table( self.db:prepare("INSERT INTO test VALUES (?, ?)") )
assert_table( stmt:bind(0, "Test") )
assert_error(function() stmt:bind("To few") end)
assert_error(function() stmt:bind(0, "Test", "To many") end)
end
function st:test_questionmark()
local stmt = assert_table( self.db:prepare("INSERT INTO test VALUES (?, ?)") )
assert_table( stmt:bind(4, "Good morning") )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning" }
assert_table( stmt:bind(5, "Foo Bar") )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_questionmark_multi()
local stmt = assert_table( self.db:prepare([[
INSERT INTO test VALUES (?, ?); INSERT INTO test VALUES (?, ?) ]]))
assert( stmt:bind(5, "Foo Bar", 4, "Good morning") )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_identifiers()
local stmt = assert_table( self.db:prepare("INSERT INTO test VALUES (:id, :name)") )
assert_table( stmt:bind(4, "Good morning") )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning" }
assert_table( stmt:bind(5, "Foo Bar") )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_identifiers_multi()
local stmt = assert_table( self.db:prepare([[
INSERT INTO test VALUES (:id1, :name1); INSERT INTO test VALUES (:id2, :name2) ]]))
assert( stmt:bind(5, "Foo Bar", 4, "Good morning") )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_identifiers_names()
local stmt = assert_table( self.db:prepare({"name", "id"}, "INSERT INTO test VALUES (:id, $name)") )
assert_table( stmt:bind("Good morning", 4) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning" }
assert_table( stmt:bind("Foo Bar", 5) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_identifiers_multi_names()
local stmt = assert_table( self.db:prepare( {"name", "id1", "id2"},[[
INSERT INTO test VALUES (:id1, $name); INSERT INTO test VALUES ($id2, :name) ]]))
assert( stmt:bind("Hoho", 4, 5) )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Hoho", "Hoho" }
end
function st:test_colon_identifiers_names()
local stmt = assert_table( self.db:prepare({":name", ":id"}, "INSERT INTO test VALUES (:id, $name)") )
assert_table( stmt:bind("Good morning", 4) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning" }
assert_table( stmt:bind("Foo Bar", 5) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_colon_identifiers_multi_names()
local stmt = assert_table( self.db:prepare( {":name", ":id1", ":id2"},[[
INSERT INTO test VALUES (:id1, $name); INSERT INTO test VALUES ($id2, :name) ]]))
assert( stmt:bind("Hoho", 4, 5) )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Hoho", "Hoho" }
end
function st:test_dollar_identifiers_names()
local stmt = assert_table( self.db:prepare({"$name", "$id"}, "INSERT INTO test VALUES (:id, $name)") )
assert_table( stmt:bind("Good morning", 4) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning" }
assert_table( stmt:bind("Foo Bar", 5) )
assert_table( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Good morning", "Foo Bar" }
end
function st:test_dollar_identifiers_multi_names()
local stmt = assert_table( self.db:prepare( {"$name", "$id1", "$id2"},[[
INSERT INTO test VALUES (:id1, $name); INSERT INTO test VALUES ($id2, :name) ]]))
assert( stmt:bind("Hoho", 4, 5) )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Hoho", "Hoho" }
end
function st:test_bind_by_names()
local stmt = assert_table( self.db:prepare("INSERT INTO test VALUES (:id, :name)") )
local args = { }
args.id = 5
args.name = "Hello girls"
assert( stmt:bind(args) )
assert( stmt:exec() )
args.id = 4
args.name = "Hello boys"
assert( stmt:bind(args) )
assert( stmt:exec() )
self:check_content{ "Hello World", "Hello Lua", "Hello sqlite3", "Hello boys", "Hello girls" }
end
--------------------------------
-- Tests binding of arguments --
--------------------------------
b = lunit.TestCase("Binding Tests")
function b:setup()
self.db = assert( sqlite3.open_memory() )
assert_table( self.db:exec("CREATE TABLE test (id, name)") )
end
function b:teardown()
assert_table( self.db:close() )
end
function b:test_auto_parameter_names()
local stmt = assert_table( self.db:prepare([[
INSERT INTO test VALUES(:a, $b);
INSERT INTO test VALUES(:a2, :b2);
INSERT INTO test VALUES($a, :b);
INSERT INTO test VALUES($a3, $b3)
]]))
local parameters = assert_table( stmt:parameter_names() )
assert_equal( 6, table.getn(parameters) )
assert_equal( "a", parameters[1] )
assert_equal( "b", parameters[2] )
assert_equal( "a2", parameters[3] )
assert_equal( "b2", parameters[4] )
assert_equal( "a3", parameters[5] )
assert_equal( "b3", parameters[6] )
end
function b:test_no_parameter_names_1()
local stmt = assert_table( self.db:prepare([[ SELECT * FROM test ]]))
local parameters = assert_table( stmt:parameter_names() )
assert_equal( 0, table.getn(parameters) )
end
function b:test_no_parameter_names_2()
local stmt = assert_table( self.db:prepare([[ INSERT INTO test VALUES(?, ?) ]]))
local parameters = assert_table( stmt:parameter_names() )
assert_equal( 0, table.getn(parameters) )
end
--------------------------------------------
-- Tests loop break and statement reusage --
--------------------------------------------
----------------------------
-- Test for bugs reported --
----------------------------
bug = lunit.TestCase("Bug-Report Tests")
function bug:setup()
self.db = assert( sqlite3.open_memory() )
end
function bug:teardown()
assert_table( self.db:close() )
end
function bug:test_1()
self.db:exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
local query = assert_table( self.db:prepare("SELECT id FROM test WHERE value=?") )
assert_table ( query:bind("1") )
assert_nil ( query:first_cols() )
assert_table ( query:bind("2") )
assert_nil ( query:first_cols() )
end
function bug:test_nils() -- appeared in lua-5.1 (holes in arrays)
local function check(arg1, arg2, arg3, arg4, arg5)
assert_equal(1, arg1)
assert_equal(2, arg2)
assert_nil(arg3)
assert_equal(4, arg4)
assert_nil(arg5)
end
self.db:set_function("test_nils", 5, function(arg1, arg2, arg3, arg4, arg5)
check(arg1, arg2, arg3, arg4, arg5)
end)
assert_table( self.db:exec([[ SELECT test_nils(1, 2, NULL, 4, NULL) ]]) )
local arg1, arg2, arg3, arg4, arg5 = self.db:first_cols([[ SELECT 1, 2, NULL, 4, NULL ]])
check(arg1, arg2, arg3, arg4, arg5)
local row = assert_table( self.db:first_irow([[ SELECT 1, 2, NULL, 4, NULL ]]) )
check(row[1], row[2], row[3], row[4], row[5])
end

View File

@@ -0,0 +1,36 @@
--[[--------------------------------------------------------------------------
Author: Michael Roth <mroth@nessie.de>
Copyright (c) 2004 Michael Roth <mroth@nessie.de>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]--------------------------------------------------------------------------
require "lunit"
require "tests-sqlite3"
require "tests-luasql"
lunit.run()