diff options
| author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-02-17 17:25:57 +0000 |
|---|---|---|
| committer | <> | 2015-03-17 16:26:24 +0000 |
| commit | 780b92ada9afcf1d58085a83a0b9e6bc982203d1 (patch) | |
| tree | 598f8b9fa431b228d29897e798de4ac0c1d3d970 /lang/sql/odbc | |
| parent | 7a2660ba9cc2dc03a69ddfcfd95369395cc87444 (diff) | |
| download | berkeleydb-master.tar.gz | |
Diffstat (limited to 'lang/sql/odbc')
63 files changed, 37599 insertions, 1117 deletions
diff --git a/lang/sql/odbc/ChangeLog b/lang/sql/odbc/ChangeLog index bde5f028..69bdd5b6 100644 --- a/lang/sql/odbc/ChangeLog +++ b/lang/sql/odbc/ChangeLog @@ -1,6 +1,110 @@ SQLite ODBC Driver ------------------ +Sun Dec 08 2013 version 0.996 released + + * update to SQLite 3.8.2 + * when length/indicator parameter to SQLBindParam*() is + a NULL pointer for SQL_CHAR, treat it like SQL_NTS + * fixes in ZIP read-only extension module + * update sqlite+tcc module to TCC 0.9.26 + * for SQLite3/4 drivers allow recursive ODBC escape sequences + * added partial implementation of SQLBulkOperations() + in SQLite3/4 drivers + * added SQL_UPDATE/SQL_DELETE/SQL_ADD handling in SQLSetPos() + of SQLite3/4 drivers + +Wed Sep 11 2013 version 0.995 released + + * update to SQLite 3.8.0.2 + * enhanced MinGW cross make/build scripts to support + generating with newer MSVC runtimes, e.g. Visual + Studio 2010 msvcr100.dll + +Tue Aug 27 2013 version 0.994 released + + * update to SQLite 3.8.0 + * enhanced fixupsql() regarding ODBC escape sequences + * fixes in Win32 dialog option handling of julian day conversion + * allow simple EXPLAIN statements to return result sets + * use sqlite3_close_v2() if available + +Thu May 23 2013 version 0.993 released + + * update to SQLite 3.7.17 + * added julian day conversion for SQL_DATE/SQL_TIME/SQL_TIMESTAMP + +Mon Apr 01 2013 version 0.992 released + + * update to SQLite 3.7.16.1 + * enhancements in XPath virtual table module + +Wed Mar 20 2013 version 0.991 released + + * update to SQLite 3.7.16 + * added experimental libxml2 XPath virtual table module + * added Michael Brey's configure fixes + * added Zac Vawter's patch for Win32 ExpandEnviromentStrings() + on database file names + * fixes for Windows 8 64bit (SetWindowLongPtr()) + * added experimental SQLite4 driver + * fixes in impexp extension module + * added manifests to adddsn.exe/inst.exe + +Fri Dec 14 2012 version 0.99 released + + * update to SQLite 3.7.15 + * added read-only support for embedded SQLite3 databases + in zipfile extension, and on Linux r/w support using + mremap() system call + * fixed missing fclose() in blob_export SQLite function + * added proper handling for SQL_DIAG_CURSOR_ROW_COUNT and + SQL_DIAG_ROW_COUNT in SQLGetDiagField() + * fixed return code for CREATE/DROP stmts in SQLPrepare() + and SQLExecDirect() thanks Peter Harvey for reporting + * fixed missing SQL_DIAG_DYNAMIC_FUNCTION case in + SQLGetDiagField(), thans Peter Harvey for reporting + * SQLGetData fixes for JDK1.7 on x86_64 + * added UN*X build of ZIP read-only extension module + +Sat Oct 06 2012 version 0.98 released + + * update to SQLite 3.7.14.1 + * fixes in floating point retrieval for MSVC, thanks + Ben van der Merwe for reporting and test + * added ZIP read-only extension module (zipfile) + * fixes in CSV table extension module + * fixes in handling default DSNs for Win32, thanks Zac Vawter + for patch + +Thu Sep 06 2012 version 0.97 released + + * update to SQLite 3.7.14 + * added CSV table extension module (csvtable) + * added handling of SQL_DATA_AT_EXEC for parameter binding + * increased file name buffers for Win32 + * length fixes in Win32 version of SQLDriverConnectW() + +Sun Jun 24 2012 version 0.96 released + + * update to SQLite 3.7.13 + * allow "PRAGMA" SQL command to return result set + * fixed UCS-2 surrogate sequences and UTF-8 translation, + thanks KamLeong Lai for fix and testing + +Sat May 19 2012 version 0.95 released + + * update to SQLite 3.7.12 + * fixed compiler warnings + * now build SQLite 2 driver for Win64, too + +Wed Jan 25 2012 version 0.94 released + + * update to SQLite 3.7.10 + * use new MinGW cross compiler for Win32/Win64 binaries + * fixed strtol issues regarding timestamp fractional parts + * filter out ODBC escape sequences {oj ...} etc. + Tue Nov 15 2011 version 0.93 released * update to SQLite 3.7.9 diff --git a/lang/sql/odbc/Makefile.in b/lang/sql/odbc/Makefile.in index 88853f18..ac96b799 100644 --- a/lang/sql/odbc/Makefile.in +++ b/lang/sql/odbc/Makefile.in @@ -27,13 +27,23 @@ SQLITE3_FLAGS = -DHAVE_SQLITE3COLUMNTABLENAME=@SQLITE3_COLUMNTABLENAME@ \ -DHAVE_SQLITE3PROFILE=@SQLITE3_PROFILE@ \ -DHAVE_SQLITE3STRNICMP=@SQLITE3_STRNICMP@ \ -DHAVE_SQLITE3TABLECOLUMNMETADATA=@SQLITE3_TABLECOLUMNMETADATA@ \ + -DHAVE_SQLITE3CLOSEV2=@SQLITE3_CLOSE_V2@ \ @DL_OPTS@ SQLITE3_A10N_C = @SQLITE3_A10N_C@ SQLITE3_A10N_O = @SQLITE3_A10N_O@ + +SQLITE4_INC = @SQLITE4_INC@ +SQLITE4_FLAGS = @SQLITE4_A10N_FLAGS@ @DL_OPTS@ +SQLITE4_A10N_C = @SQLITE4_A10N_C@ +SQLITE4_A10N_O = @SQLITE4_A10N_O@ + ODBC_FLAGS = @ODBC_FLAGS@ ODBC_LIB = @ODBC_LIB@ @LDFLAGS@ VER_INFO = @VER_INFO@ +XML2_FLAGS = @XML2_FLAGS@ +XML2_LIBS = @XML2_LIBS@ + all: @LIB_TARGETS@ libsqliteodbc.la: sqliteodbc.lo @@ -47,6 +57,11 @@ libsqlite3odbc.la: sqlite3odbc.lo $(SQLITE3_A10N_O) $(SQLITE3_LIB) $(ODBC_LIB) -release $(VER_INFO) \ @DL_INITFINI@ +libsqlite4odbc.la: sqlite4odbc.lo $(SQLITE4_A10N_O) + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libsqlite4odbc.la \ + sqlite4odbc.lo $(SQLITE4_A10N_O) -rpath $(drvdir) \ + $(ODBC_LIB) -release $(VER_INFO) @DL_INITFINI@ + libsqlite3_mod_blobtoxy.la: blobtoxy.lo $(LIBTOOL) --mode=link $(CC) $(CFLAGS) \ -o libsqlite3_mod_blobtoxy.la \ @@ -57,6 +72,21 @@ libsqlite3_mod_impexp.la: impexp.lo -o libsqlite3_mod_impexp.la \ impexp.lo -rpath $(drvdir) -release $(VER_INFO) +libsqlite3_mod_csvtable.la: csvtable.lo + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) \ + -o libsqlite3_mod_csvtable.la \ + csvtable.lo -rpath $(drvdir) -release $(VER_INFO) + +libsqlite3_mod_zipfile.la: zipfile.lo + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) \ + -o libsqlite3_mod_zipfile.la \ + zipfile.lo -rpath $(drvdir) -release $(VER_INFO) -lz + +libsqlite3_mod_xpath.la: xpath.lo + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) \ + -o libsqlite3_mod_xpath.la \ + xpath.lo -rpath $(drvdir) -release $(VER_INFO) $(XML2_LIBS) + sqliteodbc.lo: sqliteodbc.c sqliteodbc.h $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ -I$(SQLITE_INC) $(ODBC_FLAGS) \ @@ -67,9 +97,18 @@ sqlite3odbc.lo: sqlite3odbc.c sqlite3odbc.h -I$(SQLITE3_INC) $(ODBC_FLAGS) \ $(SQLITE3_FLAGS) sqlite3odbc.c +sqlite4odbc.lo: sqlite4odbc.c sqlite4odbc.h + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ + -I$(SQLITE4_INC) $(ODBC_FLAGS) \ + $(SQLITE4_FLAGS) sqlite4odbc.c + $(SQLITE3_A10N_O): $(SQLITE3_A10N_C) $(LIBTOOL) --mode=compile $(CC) -c $(CFLAGS) $(SQLITE3_A10N_C) +$(SQLITE4_A10N_O): $(SQLITE4_A10N_C) + $(LIBTOOL) --mode=compile $(CC) -c $(CFLAGS) \ + @SQLITE4_A10N_FLAGS@ $(SQLITE4_A10N_C) + blobtoxy.lo: blobtoxy.c $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ -I$(SQLITE3_INC) \ @@ -80,6 +119,21 @@ impexp.lo: impexp.c -I$(SQLITE3_INC) \ $(SQLITE3_FLAGS) impexp.c +csvtable.lo: csvtable.c + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ + -I$(SQLITE3_INC) \ + $(SQLITE3_FLAGS) csvtable.c + +zipfile.lo: zipfile.c + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ + -I$(SQLITE3_INC) \ + $(SQLITE3_FLAGS) zipfile.c + +xpath.lo: xpath.c + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c \ + -I$(SQLITE3_INC) \ + $(SQLITE3_FLAGS) $(XML2_FLAGS) xpath.c + install-2: libsqliteodbc.la $(LIBTOOL) --mode=install $(INSTALL) \ libsqliteodbc.la $(DESTDIR)$(drvdir) @@ -95,7 +149,8 @@ uninstall-2: $(LIBTOOL) --mode=uninstall \ $(RM) $(DESTDIR)$(drvdir)/libsqliteodbc.la -install-3: libsqlite3odbc.la @EXT_BLOBTOXY@ @EXT_IMPEXP@ +install-3: libsqlite3odbc.la @EXT_BLOBTOXY@ @EXT_IMPEXP@ \ + @EXT_CSVTABLE@ @EXT_ZIPFILE@ @EXT_XPATH@ $(LIBTOOL) --mode=install $(INSTALL) \ libsqlite3odbc.la $(DESTDIR)$(drvdir) test -z "@EXT_BLOBTOXY@" || \ @@ -104,6 +159,15 @@ install-3: libsqlite3odbc.la @EXT_BLOBTOXY@ @EXT_IMPEXP@ test -z "@EXT_IMPEXP@" || \ $(LIBTOOL) --mode=install $(INSTALL) \ @EXT_IMPEXP@ $(DESTDIR)$(drvdir) + test -z "@EXT_CSVTABLE@" || \ + $(LIBTOOL) --mode=install $(INSTALL) \ + @EXT_CSVTABLE@ $(DESTDIR)$(drvdir) + test -z "@EXT_ZIPFILE@" || \ + $(LIBTOOL) --mode=install $(INSTALL) \ + @EXT_ZIPFILE@ $(DESTDIR)$(drvdir) + test -z "@EXT_XPATH@" || \ + $(LIBTOOL) --mode=install $(INSTALL) \ + @EXT_XPATH@ $(DESTDIR)$(drvdir) uninstall-3: $(LIBTOOL) --mode=uninstall \ @@ -114,6 +178,15 @@ uninstall-3: test -z "@EXT_IMPEXP@" || \ $(LIBTOOL) --mode=uninstall \ $(RM) $(DESTDIR)$(drvdir)/@EXT_IMPEXP@ + test -z "@EXT_CSVTABLE@" || \ + $(LIBTOOL) --mode=uninstall \ + $(RM) $(DESTDIR)$(drvdir)/@EXT_CSVTABLE@ + test -z "@EXT_ZIPFILE@" || \ + $(LIBTOOL) --mode=uninstall \ + $(RM) $(DESTDIR)$(drvdir)/@EXT_ZIPFILE@ + test -z "@EXT_XPATH@" || \ + $(LIBTOOL) --mode=uninstall \ + $(RM) $(DESTDIR)$(drvdir)/@EXT_XPATH@ drvinst-3: install-3 sh drvdsninst.sh SQLITE3 "SQLite3 Datasource" \ @@ -122,6 +195,22 @@ drvinst-3: install-3 drvuninst-3: uninstall-3 sh drvdsnuninst.sh SQLITE3 "SQLite3 Datasource" + +install-4: libsqlite4odbc.la + $(LIBTOOL) --mode=install $(INSTALL) \ + libsqlite4odbc.la $(DESTDIR)$(drvdir) + +uninstall-4: + $(LIBTOOL) --mode=uninstall \ + $(RM) $(DESTDIR)$(drvdir)/libsqlite4odbc.la + +drvinst-4: install-4 + sh drvdsninst.sh SQLITE4 "SQLite4 Datasource" \ + libsqlite4odbc.la $(drvdir) + +drvuninst-4: uninstall-4 + sh drvdsnuninst.sh SQLITE4 "SQLite4 Datasource" + install: @INST_TARGETS@ uninstall: @UNINST_TARGETS@ diff --git a/lang/sql/odbc/Makefile.mingw-cross b/lang/sql/odbc/Makefile.mingw-cross new file mode 100644 index 00000000..4262e694 --- /dev/null +++ b/lang/sql/odbc/Makefile.mingw-cross @@ -0,0 +1,307 @@ +# Makefile for SQLite ODBC Drivers +# using MinGW cross compiler + +MINGW = /opt/mingw64/bin/x86_64-w64-mingw32- +CC = $(MINGW)gcc -m32 -march=i386 -mtune=i386 +STRIP = $(MINGW)strip +RC = $(MINGW)windres -F pe-i386 +MAKENSIS = makensis + +DRV_VER= $(shell cat VERSION) + +CFLAGS= -O2 -Wall -DNDEBUG=1 -DDRIVER_VER_INFO=\"$(DRV_VER)\" + +ifeq ($(MSVCRT),70) + CFLAGS += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + CFLAGS += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + CFLAGS += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 -lmsvcrt +endif +ifeq ($(MSVCRT),100) + CFLAGS += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 -lmsvcrt +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +SQLITE_INC = sqlite +SQLITE_LIB = sqlite/libsqlite.a +SQLITE_FLAGS = -DHAVE_LIBVERSION=1 \ + -DHAVE_ENCDEC=1 \ + -DHAVE_SQLITEATOF=1 \ + -DHAVE_SQLITEMPRINTF=1 \ + -DHAVE_SQLITETRACE=1 + +SQLITE3_INC = sqlite3 +SQLITE3_SRC = sqlite3/src +SQLITE3_LIB = sqlite3/libsqlite3.a +SQLITE3_FLAGS= -DHAVE_SQLITE3COLUMNTABLENAME=1 \ + -DHAVE_SQLITE3LOADEXTENSION=1 \ + -DHAVE_SQLITE3PREPAREV2=1 \ + -DHAVE_SQLITE3VFS=1 \ + -DHAVE_SQLITE3PROFILE=1 \ + -DHAVE_SQLITE3CLOSEV2=1 \ + -DHAVE_SQLITE3STRNICMP=1 +SQLITE3_A10N = sqlite3/sqlite3.c +SQLITE3_A10N_FLAGS = \ + -DWIN32=1 -DNDEBUG=1 -DNO_TCL -DTHREADSAFE=1 \ + -DSQLITE_ENABLE_COLUMN_METADATA=1 \ + -DSQLITE_DLL=1 \ + -DSQLITE_THREADSAFE=1 \ + -DSQLITE_OS_WIN=1 \ + -DSQLITE_ASCII=1 \ + -DSQLITE_SOUNDEX=1 + +TCC_INC = TCC/libtcc +TCC_LIB = TCC/libtcc/libtcc.a + + +ODBC_FLAGS = -DHAVE_LONG_LONG=1 -DHAVE_SQLROWOFFSET=1 +ODBC_LIB = -lodbc + +all: sqliteodbc.dll sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + sqlite.exe \ + inst.exe instq.exe uninst.exe uninstq.exe \ + adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe \ + SQLiteODBCInstaller.exe $(SQLITE3_EXE) $(SQLITE_TCC_DLL) + +all_no2: sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + inst.exe instq.exe uninst.exe uninstq.exe \ + adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe \ + SQLiteODBCInstaller.exe $(SQLITE3_EXE) $(SQLITE_TCC_DLL) + + +sqliteodbc.o: sqliteodbc.c sqliteodbc.h resource.h + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE_INC) $(ODBC_FLAGS) \ + $(SQLITE_FLAGS) $(ADD_CFLAGS) sqliteodbc.c + +sqliteodbcu.o: sqliteodbc.c sqliteodbc.h resource.h + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE_INC) $(ODBC_FLAGS) \ + $(SQLITE_FLAGS) $(ADD_CFLAGS) -o sqliteodbcu.o sqliteodbc.c + +sqliteodbc.dll: sqliteodbc.o sqliteodbcres.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqliteodbc.a -Wl,--strip-all \ + -o sqliteodbc.dll \ + sqliteodbc.o sqliteodbcres.o \ + $(LMSVCRT) $(SQLITE_LIB) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +sqlite.exe: sqliteodbc.dll sqlite/src/minshell.c sqlite/sqlite.h \ + sqliteres.o + $(CC) $(CFLAGS) $(SQLITE_FLAGS) -Isqlite -o sqlite.exe \ + sqlite/src/minshell.c sqliteres.o -L. -lsqliteodbc \ + $(SQLITE_LIB) + $(STRIP) sqlite.exe + +sqliteodbcu.dll: sqliteodbcu.o sqliteodbcures.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqliteodbcu.a -Wl,--strip-all \ + -o sqliteodbcu.dll \ + sqliteodbcu.o sqliteodbcures.o \ + $(LMSVCRT) $(SQLITE_LIB) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +sqliteu.exe: sqliteodbcu.dll sqlite/src/minshell.c sqlite/sqlite.h \ + sqliteres.o + $(CC) $(CFLAGS) -Isqlite -o sqliteu.exe \ + sqlite/src/minshell.c sqliteres.o -L. -lsqliteodbcu \ + $(SQLITE_LIB) + $(STRIP) sqliteu.exe + +sqliteodbcres.o: sqliteodbc.rc resource.h + $(RC) -o sqliteodbcres.o -I$(SQLITE_INC) sqliteodbc.rc + +sqliteodbcures.o: sqliteodbc.rc resource.h + $(RC) -o sqliteodbcures.o -I$(SQLITE_INC) sqliteodbc.rc + +resource.h: resource.h.in + VERS=`cat VERSION` ;\ + VERS_C=`echo $$VERS | sed -e 's/\([0-9]\+\)[.]\([0-9]\+\).*/\1,\2/g'` ;\ + sed -e 's/--VERS_C--/'$$VERS_C'/g' < resource.h.in | \ + sed -e 's/--VERS--/'$$VERS'/g' > resource.h + +sqlite3a10n.o: $(SQLITE3_A10N) + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(SQLITE3_A10N_FLAGS) \ + $(ADD_CFLAGS) -o sqlite3a10n.o $(SQLITE3_A10N) + +sqlite3odbc.o: sqlite3odbc.c sqlite3odbc.h resource3.h + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(ODBC_FLAGS) \ + $(SQLITE3_FLAGS) $(ADD_CFLAGS) sqlite3odbc.c + +sqlite3odbcnw.o: sqlite3odbc.c sqlite3odbc.h resource3.h + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(ODBC_FLAGS) \ + $(SQLITE3_FLAGS) $(ADD_CFLAGS) -DWITHOUT_WINTERFACE=1 \ + -o sqlite3odbcnw.o sqlite3odbc.c + +sqlite3odbc$(SEEEXT).dll: sqlite3odbc.o sqlite3odbcres.o $(SQLITE3_A10N_O) + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3odbc.a -Wl,--strip-all \ + -o sqlite3odbc$(SEEEXT).dll \ + sqlite3odbc.o sqlite3odbcres.o $(SQLITE3_A10N_O) \ + $(SQLITE3_DLL) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + $(STRIP) sqlite3odbc$(SEEEXT).dll + +sqlite3odbc$(SEEEXT)nw.dll: sqlite3odbcnw.o sqlite3odbcres.o $(SQLITE3_A10N_O) + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3odbcnw.a -Wl,--strip-all \ + -o sqlite3odbc$(SEEEXT)nw.dll \ + sqlite3odbcnw.o sqlite3odbcres.o $(SQLITE3_A10N_O) \ + $(SQLITE3_DLL) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + $(STRIP) sqlite3odbc$(SEEEXT)nw.dll + +sqlite3.exe: sqlite3odbc$(SEEEXT).dll sqlite3/src/minshell.c \ + sqliteres.o + $(CC) $(CFLAGS) $(SQLITE3_FLAGS) -Isqlite3 -o sqlite3.exe \ + sqlite3/src/minshell.c sqliteres.o \ + -L. -lsqlite3odbc$(SEEEXT) + $(STRIP) sqlite3.exe + +sqlite3odbcres.o: sqlite3odbc.rc resource3.h + $(RC) -o sqlite3odbcres.o -I$(SQLITE3_INC) sqlite3odbc.rc + +resource3.h: resource.h.in + VERS=`cat VERSION` ;\ + VERS_C=`echo $$VERS | sed -e 's/\([0-9]\+\)[.]\([0-9]\+\).*/\1,\2/g'` ;\ + sed -e 's/--VERS_C--/'$$VERS_C'/g' < resource.h.in | \ + sed -e 's/--VERS--/'$$VERS'/g' > resource3.h + +sqliteres.rc: + @echo "ico ICON sqlite.ico" > sqliteres.rc + +sqliteres.o: sqliteres.rc + $(RC) -o sqliteres.o sqliteres.rc + +instres.o: inst.rc + $(RC) -o instres.o -I$(SQLITE3_INC) inst.rc + +inst.exe: inst.c instres.o + $(CC) $(CFLAGS) $(ADD_CFLAGS) -mwindows -o inst.exe \ + inst.c instres.o -lodbc32 -lodbccp32 -lkernel32 \ + -luser32 + $(STRIP) inst.exe + +instq.exe: inst.exe + cp -p inst.exe instq.exe + +uninst.exe: inst.exe + cp -p inst.exe uninst.exe + +uninstq.exe: inst.exe + cp -p inst.exe uninstq.exe + +adddsnres.o: adddsn.rc + $(RC) -o adddsnres.o -I$(SQLITE3_INC) adddsn.rc + +adddsn.exe: adddsn.c adddsnres.o + $(CC) $(CFLAGS) $(ADD_CFLAGS) -mwindows -o adddsn.exe \ + adddsn.c adddsnres.o -lodbc32 -lodbccp32 -lkernel32 \ + -luser32 + $(STRIP) adddsn.exe + +remdsn.exe: adddsn.exe + cp -p adddsn.exe remdsn.exe + +addsysdsn.exe: adddsn.exe + cp -p adddsn.exe addsysdsn.exe + +remsysdsn.exe: adddsn.exe + cp -p adddsn.exe remsysdsn.exe + +SQLiteODBCInstaller.exe: SQLiteODBCInstaller.c sqliteres.o + $(CC) $(CFLAGS) $(ADD_CFLAGS) -o SQLiteODBCInstaller.exe \ + SQLiteODBCInstaller.c sqliteres.o -lkernel32 -luser32 + $(STRIP) SQLiteODBCInstaller.exe + +blobtoxyres.o: blobtoxy.rc resource3.h + $(RC) -o blobtoxyres.o -I$(SQLITE3_INC) blobtoxy.rc + +blobtoxy.o: blobtoxy.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + blobtoxy.c + +sqlite3_mod_blobtoxy.dll: blobtoxy.o blobtoxyres.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_blobtoxy.dll \ + blobtoxy.o blobtoxyres.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +impexp.o: impexp.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + impexp.c + +sqlite3_mod_impexp.dll: impexp.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_impexp.dll \ + impexp.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +csvtable.o: csvtable.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + csvtable.c + +sqlite3_mod_csvtable.dll: csvtable.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_csvtable.dll \ + csvtable.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +zipfile.o: zipfile.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + -Izlib zipfile.c + +sqlite3_mod_zipfile.dll: zipfile.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_zipfile.dll \ + zipfile.o -Lzlib -lz $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +sqlite+tcc.o: sqlite+tcc.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + -I$(TCC_INC) sqlite+tcc.c + +sqlite+tcc.dll: sqlite+tcc.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite+tcc.dll \ + sqlite+tcc.o $(TCC_LIB) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +clean: + rm -f *.o sqliteodbc*.dll sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + sqlite+tcc.dll \ + *inst.exe *dsn.exe sqlite*.exe sqliteres.rc *~ \ + core core.* + rm -f resource.h resource3.h + diff --git a/lang/sql/odbc/Makefile.mingw64-cross b/lang/sql/odbc/Makefile.mingw64-cross new file mode 100644 index 00000000..a1f19e65 --- /dev/null +++ b/lang/sql/odbc/Makefile.mingw64-cross @@ -0,0 +1,288 @@ +# Makefile for SQLite3 ODBC Drivers +# using MinGW WIN64 cross compiler + +MINGW = /opt/mingw64/bin/x86_64-w64-mingw32- +CC = $(MINGW)gcc +STRIP = $(MINGW)strip +RC = $(MINGW)windres +MAKENSIS = makensis + +DRV_VER= $(shell cat VERSION) + +CFLAGS= -O2 -Wall -DNDEBUG=1 -DDRIVER_VER_INFO=\"$(DRV_VER)\" + +ifeq ($(MSVCRT),70) + CFLAGS += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + CFLAGS += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + CFLAGS += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 +endif +ifeq ($(MSVCRT),100) + CFLAGS += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +SQLITE_INC = sqlite +SQLITE_LIB = sqlite/libsqlite.a +SQLITE_FLAGS = -DHAVE_LIBVERSION=1 \ + -DHAVE_ENCDEC=1 \ + -DHAVE_SQLITEATOF=1 \ + -DHAVE_SQLITEMPRINTF=1 \ + -DHAVE_SQLITETRACE=1 + +SQLITE3_INC = sqlite3 +SQLITE3_SRC = sqlite3/src +SQLITE3_LIB = sqlite3/libsqlite3.a +SQLITE3_FLAGS= -DHAVE_SQLITE3COLUMNTABLENAME=1 \ + -DHAVE_SQLITE3LOADEXTENSION=1 \ + -DHAVE_SQLITE3PREPAREV2=1 \ + -DHAVE_SQLITE3VFS=1 \ + -DHAVE_SQLITE3PROFILE=1 \ + -DHAVE_SQLITE3CLOSEV2=1 \ + -DHAVE_SQLITE3STRNICMP=1 +SQLITE3_A10N = sqlite3/sqlite3.c +SQLITE3_A10N_FLAGS = \ + -DWIN32=1 -DNDEBUG=1 -DNO_TCL -DTHREADSAFE=1 \ + -DSQLITE_ENABLE_COLUMN_METADATA=1 \ + -DSQLITE_DLL=1 \ + -DSQLITE_THREADSAFE=1 \ + -DSQLITE_OS_WIN=1 \ + -DSQLITE_ASCII=1 \ + -DSQLITE_SOUNDEX=1 + +ODBC_FLAGS = -DHAVE_LONG_LONG=1 -DHAVE_SQLROWOFFSET=1 -DHAVE_SQLLEN=1 \ + -DHAVE_SQLULEN=1 -DHAVE_SQLROWCOUNT=1 -DHAVE_SQLSETPOSIROW=1 \ + -DPTRDIFF_T=ptrdiff_t +ODBC_LIB = -lodbc + +all: sqliteodbc.dll sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + sqlite.exe \ + inst.exe instq.exe uninst.exe uninstq.exe \ + adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe $(SQLITE3_EXE) + + +all_no2: sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + inst.exe instq.exe uninst.exe uninstq.exe \ + adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe $(SQLITE3_EXE) + +sqliteodbc.o: sqliteodbc.c sqliteodbc.h resource.h + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE_INC) $(ODBC_FLAGS) \ + $(SQLITE_FLAGS) $(ADD_CFLAGS) sqliteodbc.c + +sqliteodbcu.o: sqliteodbc.c sqliteodbc.h resource.h + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE_INC) $(ODBC_FLAGS) \ + $(SQLITE_FLAGS) $(ADD_CFLAGS) -o sqliteodbcu.o sqliteodbc.c + +sqliteodbc.dll: sqliteodbc.o sqliteodbcres.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqliteodbc.a -Wl,--strip-all \ + -o sqliteodbc.dll \ + sqliteodbc.o sqliteodbcres.o \ + $(LMSVCRT) $(SQLITE_LIB) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +sqlite.exe: sqliteodbc.dll sqlite/src/minshell.c sqlite/sqlite.h \ + sqliteres.o + $(CC) $(CFLAGS) $(SQLITE_FLAGS) -o sqlite.exe \ + sqlite/src/minshell.c sqliteres.o -L. -lsqliteodbc \ + $(SQLITE_LIB) + $(STRIP) sqlite.exe + +sqliteodbcu.dll: sqliteodbcu.o sqliteodbcures.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqliteodbcu.a -Wl,--strip-all \ + -o sqliteodbcu.dll \ + sqliteodbcu.o sqliteodbcures.o \ + $(LMSVCRT) $(SQLITE_LIB) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +sqliteu.exe: sqliteodbc.dll sqlite/src/minshell.c sqlite/sqlite.h \ + sqliteres.o + $(CC) $(CFLAGS) $(SQLITE_FLAGS) -o sqliteu.exe \ + sqlite/src/minshell.c sqliteres.o -L. -lsqliteodbcu \ + $(SQLITE_LIB) + $(STRIP) sqliteu.exe + +sqliteodbcres.o: sqliteodbc.rc resource.h + $(RC) -o sqliteodbcres.o -I$(SQLITE_INC) sqliteodbc.rc + +sqliteodbcures.o: sqliteodbc.rc resource.h + $(RC) -o sqliteodbcures.o -I$(SQLITE_INC) sqliteodbc.rc + +resource.h: resource.h.in + VERS=`cat VERSION` ;\ + VERS_C=`echo $$VERS | sed -e 's/\([0-9]\+\)[.]\([0-9]\+\).*/\1,\2/g'` ;\ + sed -e 's/--VERS_C--/'$$VERS_C'/g' < resource.h.in | \ + sed -e 's/--VERS--/'$$VERS'/g' > resource.h + +sqlite3a10n.o: $(SQLITE3_A10N) + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(SQLITE3_A10N_FLAGS) \ + $(ADD_CFLAGS) -o sqlite3a10n.o $(SQLITE3_A10N) + +sqlite3odbc.o: sqlite3odbc.c sqlite3odbc.h resource3.h + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(ODBC_FLAGS) \ + $(SQLITE3_FLAGS) $(ADD_CFLAGS) sqlite3odbc.c + +sqlite3odbcnw.o: sqlite3odbc.c sqlite3odbc.h resource3.h + $(CC) $(CFLAGS) -c -I$(SQLITE3_INC) $(ODBC_FLAGS) \ + $(SQLITE3_FLAGS) $(ADD_CFLAGS) -DWITHOUT_WINTERFACE=1 \ + -o sqlite3odbcnw.o sqlite3odbc.c + +sqlite3odbc$(SEEEXT).dll: sqlite3odbc.o sqlite3odbcres.o \ + $(SQLITE3_A10N_O) + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3odbc.a -Wl,--strip-all \ + -o sqlite3odbc$(SEEEXT).dll \ + sqlite3odbc.o sqlite3odbcres.o $(SQLITE3_A10N_O) \ + $(SQLITE3_DLL) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + $(STRIP) sqlite3odbc$(SEEEXT).dll + +sqlite3odbc$(SEEEXT)nw.dll: sqlite3odbcnw.o sqlite3odbcres.o \ + $(SQLITE3_A10N_O) + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3odbcnw.a -Wl,--strip-all \ + -o sqlite3odbc$(SEEEXT)nw.dll \ + sqlite3odbcnw.o sqlite3odbcres.o $(SQLITE3_A10N_O) \ + $(SQLITE3_DLL) $(LMSVCRT) \ + -lodbc32 -lodbccp32 -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + $(STRIP) sqlite3odbc$(SEEEXT)nw.dll + +sqlite3.exe: sqlite3odbc$(SEEEXT).dll sqlite3/src/minshell.c \ + sqliteres.o + $(CC) $(CFLAGS) $(SQLITE3_FLAGS) -Isqlite3 -o sqlite3.exe \ + sqlite3/src/minshell.c sqliteres.o \ + -L. -lsqlite3odbc$(SEEEXT) + $(STRIP) sqlite3.exe + +sqlite3odbcres.o: sqlite3odbc.rc resource3.h + $(RC) -o sqlite3odbcres.o -I$(SQLITE3_INC) sqlite3odbc.rc + +resource3.h: resource.h.in + VERS=`cat VERSION` ;\ + VERS_C=`echo $$VERS | sed -e 's/\([0-9]\+\)[.]\([0-9]\+\).*/\1,\2/g'` ;\ + sed -e 's/--VERS_C--/'$$VERS_C'/g' < resource.h.in | \ + sed -e 's/--VERS--/'$$VERS'/g' > resource3.h + +sqliteres.rc: + @echo "ico ICON sqlite.ico" > sqliteres.rc + +sqliteres.o: sqliteres.rc + $(RC) -o sqliteres.o sqliteres.rc + +instres.o: inst.rc + $(RC) -o instres.o -I$(SQLITE3_INC) inst.rc + +inst.exe: inst.c instres.o + $(CC) $(CFLAGS) $(ADD_CFLAGS) -mwindows -o inst.exe \ + inst.c instres.o -lodbc32 -lodbccp32 -lkernel32 \ + -luser32 + $(STRIP) inst.exe + +instq.exe: inst.exe + cp -p inst.exe instq.exe + +uninst.exe: inst.exe + cp -p inst.exe uninst.exe + +uninstq.exe: inst.exe + cp -p inst.exe uninstq.exe + +adddsnres.o: adddsn.rc + $(RC) -o adddsnres.o -I$(SQLITE3_INC) adddsn.rc + +adddsn.exe: adddsn.c adddsnres.o + $(CC) $(CFLAGS) $(ADD_CFLAGS) -mwindows -o adddsn.exe \ + adddsn.c adddsnres.o -lodbc32 -lodbccp32 -lkernel32 \ + -luser32 + $(STRIP) adddsn.exe + +remdsn.exe: adddsn.exe + cp -p adddsn.exe remdsn.exe + +addsysdsn.exe: adddsn.exe + cp -p adddsn.exe addsysdsn.exe + +remsysdsn.exe: adddsn.exe + cp -p adddsn.exe remsysdsn.exe + +blobtoxyres.o: blobtoxy.rc resource3.h + $(RC) -o blobtoxyres.o -I$(SQLITE3_INC) blobtoxy.rc + +blobtoxy.o: blobtoxy.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + blobtoxy.c + +sqlite3_mod_blobtoxy.dll: blobtoxy.o blobtoxyres.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_blobtoxy.dll \ + blobtoxy.o blobtoxyres.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +impexp.o: impexp.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + impexp.c + +sqlite3_mod_impexp.dll: impexp.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_impexp.dll \ + impexp.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +csvtable.o: csvtable.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + csvtable.c + +sqlite3_mod_csvtable.dll: csvtable.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_csvtable.dll \ + csvtable.o $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +zipfile.o: zipfile.c + $(CC) $(CFLAGS) -mdll -c -I$(SQLITE3_INC) -I$(SQLITE3_SRC) \ + -Izlib zipfile.c + +sqlite3_mod_zipfile.dll: zipfile.o + $(CC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--strip-all -o sqlite3_mod_zipfile.dll \ + zipfile.o -Lzlib -lz $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +clean: + rm -f *.o sqliteodbc*.dll sqlite3odbc$(SEEEXT).dll \ + sqlite3_mod_blobtoxy.dll \ + sqlite3_mod_impexp.dll \ + sqlite3_mod_csvtable.dll \ + sqlite3_mod_zipfile.dll \ + *inst.exe *dsn.exe sqlite*.exe sqliteres.rc *~ \ + core core.* + rm -f resource.h resource3.h + diff --git a/lang/sql/odbc/README b/lang/sql/odbc/README index 7a2af0c3..c307a322 100644 --- a/lang/sql/odbc/README +++ b/lang/sql/odbc/README @@ -3,9 +3,8 @@ SQLite ODBC Driver This is an open source ODBC driver for the wonderful
SQLite 2.8.* and SQLite 3.* Database Engine/Library.
-The driver is usable but may contain lots of memory
-leaks and all other kinds of bugs. Use it on your own
-risk.
+The driver is usable but may contain bugs. Use it on
+your own risk.
The current source can be downloaded from
@@ -15,7 +14,7 @@ WIN32 binaries (the ODBC driver DLL, install/uninstall programs) are in http://www.ch-werner.de/sqliteodbc/sqliteodbc.exe
-The binaries were made with SQLite 2.8.17, SQLite 3.7.8, MingW
+The binaries were made with SQLite 2.8.17, SQLite 3.8.2, MingW
cross compiler and tested on Windows NT 4.0 with the query tool
of MS Excel 97, with StarOffice 5.2 and OpenOffice 1.1 and 2.x.
Execute the sqliteodbc.exe NSIS installer to unpack the necessary
@@ -28,7 +27,7 @@ on as an absolute pathname including the drive letter, eg as can be specified in the respective field. If empty a default value
of 100000 milliseconds is used.
-The Win64 installer (sqliteodbc_w64.exe) was made with SQLite 3.7.8,
+The Win64 installer (sqliteodbc_w64.exe) was made with SQLite 3.8.2,
MingW cross compiler and only rudimentary tested on Windows Vista 64.
Other tests were made on Linux with the "isql" command line tool
@@ -66,7 +65,10 @@ Restrictions of data type mapping: - The driver puts the ODBC string representations for date/time,
(eg for "{ts '2001-10-10 12:58:00'}" the substring within the
single quotes) directly into the SQLite column
-
+- When the DSN Option "JDConv" (Julian Day conversion) is enabled
+ the SQLite 3 driver translates floating point column data
+ interpreted as Julian Day to/from SQL_DATE, SQL_TIME, and
+ SQL_TIMESTAMP data types (supported since May 2013)
Since November 17th, 2001, configure/libtool is used for the Un*x
version which should automatically find the SQLite and unixODBC
@@ -329,7 +331,7 @@ TODO: - improve documentation
-2011-11-15
+2013-12-08
Christian Werner
mailto:chw@ch-werner.de
diff --git a/lang/sql/odbc/README.OS2 b/lang/sql/odbc/README.OS2 new file mode 100644 index 00000000..2ea539d9 --- /dev/null +++ b/lang/sql/odbc/README.OS2 @@ -0,0 +1,22 @@ +The OS/2 port of the sqlite ODBC driver is compiled with
+the GCC 3.2.2 compiler. The makefile is set up to use
+the unixODBC header files for common definitions of the
+ODBC driver manager.
+
+The port uses the SQLite 2.8.15 OS/2 port package done by
+Andrew MacIntyre
+email: andymac@bullseye.apana.org.au, or andymac@pcug.org.au
+web: http://www.andymac.org/
+
+The build file requires the presence of of a subdirectory
+containing sqlite.h and sqlite.lib from the SQLite-2.8.15
+package.
+
+There are three variables at the beginning of the make file
+for OS/2 that have to set for your environment
+SQLITELIB - location of the SQLite.h and SQLite.lib files
+UNIXODBCROOT - root directory of the unixodbc source tree
+this is used to locate the 'include' and 'odbcinst' directories
+KITDLL - location to copy the SQLLODBC.DLL file after it is built
+
+
diff --git a/lang/sql/odbc/README.ic b/lang/sql/odbc/README.ic new file mode 100644 index 00000000..df203bb7 --- /dev/null +++ b/lang/sql/odbc/README.ic @@ -0,0 +1,51 @@ +------------------------------------------------------------------ + How to compile Win32 SQLite3 ODBC driver with the Intel-Compiler +------------------------------------------------------------------ + +This HOWTO was written by Gunter Hinrichsen <frontier.user@gmx.net> + + +Read first the README section "Build instructions for MS Visual C++ 6.0:". +Some easy changes have to be made to compile SQLite3 ODBC driver under Intel- +Compiler environment. You have to replace some options in the two make files. +Of course you can add at advanced Intel-Compiler optimize options, too. + +sqlite3.mak: +------------ +- Replace in "BCC" the "cl" by "icl". +Old: "BCC = cl -Gs -GX -D_WIN32 -nologo -Zi" +New: "BCC = icl -Gs -GX -D_WIN32 -nologo -Zi" + +- Replace in "TCC" the "cl" by "icl". +Old: "TCC = cl -Gs -GX -D_WIN32 -DOS_WIN=1 -nologo -Zi" +New: "TCC = icl -Gs -GX -D_WIN32 -DOS_WIN=1 -nologo -Zi" + +- Replace in the "sqlite3.dll:" section "link" by "xilink". +Old: "xilink -release -nodefaultlib -dll msvcrt.lib kernel32.lib \" +New: "link -release -nodefaultlib -dll msvcrt.lib kernel32.lib \" + +- Add in the "xilink" comando line the lib "libircmt.lib". +Old: "xilink -release -nodefaultlib -dll msvcrt.lib kernel32.lib \" +New: "link -release -nodefaultlib -dll msvcrt.lib kernel32.lib libircmt.lib \" + +sqlite3odbc.mak: +---------------- +- Replace in "CC" the "cl" by "icl". +Old: "CC=cl" +New: "CC=icl" + +- Replace in "LN" the "link" by "xilink". +Old: "LN=link" +New: "LN=xilink" + +- Add in "DLLLIBS" the lib "libircmt.lib". +Old: "DLLLIBS= msvcrt.lib odbccp32.lib kernel32.lib \" +New: "DLLLIBS= msvcrt.lib odbccp32.lib kernel32.lib libircmt.lib \" + + + +------------------------------------- +Start the Intel Environment. You can find the Environment in the start-menu: +"Intel(R) Software Development Tools/Intel(R) C++ Compiler 9.1/Build Environment for IA-32 applications" +Now compile with: +nmake -f sqlite3odbc.mak diff --git a/lang/sql/odbc/SQLiteODBCInstaller.c b/lang/sql/odbc/SQLiteODBCInstaller.c index fb93e2a2..abe32bfe 100644 --- a/lang/sql/odbc/SQLiteODBCInstaller.c +++ b/lang/sql/odbc/SQLiteODBCInstaller.c @@ -449,13 +449,9 @@ int main(int argc, char* argv[]) unsigned char ParseArg(int argc, char* argv[], struct ParstArgsData_Struct *pDestArg, unsigned char MaxDestArg ) { unsigned char ArgCount; // Counters - unsigned char Status; // Status of parsing: 0x01 value needed int DestArg; // Count of parst arguments char *pValues; // Pointer to values - // Nothing startet - Status = 0; - // No args parst yet DestArg = -1; diff --git a/lang/sql/odbc/VERSION b/lang/sql/odbc/VERSION index 341ad3c8..00445f81 100644 --- a/lang/sql/odbc/VERSION +++ b/lang/sql/odbc/VERSION @@ -1 +1 @@ -0.93 +0.996 diff --git a/lang/sql/odbc/adddsn.c b/lang/sql/odbc/adddsn.c index f99aef12..8575f9c1 100644 --- a/lang/sql/odbc/adddsn.c +++ b/lang/sql/odbc/adddsn.c @@ -2,9 +2,9 @@ * @file adddsn.c * DSN creation utility for Win32. * - * $Id: adddsn.c,v 1.6 2011/03/09 05:41:15 chw Exp chw $ + * $Id: adddsn.c,v 1.8 2013/01/11 12:19:55 chw Exp chw $ * - * Copyright (c) 2003-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2003-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a diff --git a/lang/sql/odbc/adddsn.manifest b/lang/sql/odbc/adddsn.manifest new file mode 100644 index 00000000..bad81619 --- /dev/null +++ b/lang/sql/odbc/adddsn.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" name="addsn" type="win32"/> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="requireAdministrator"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly> diff --git a/lang/sql/odbc/adddsn.rc b/lang/sql/odbc/adddsn.rc new file mode 100644 index 00000000..5b487f91 --- /dev/null +++ b/lang/sql/odbc/adddsn.rc @@ -0,0 +1,62 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite3.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (63) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLiteODBC DSN Manager\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "adddsn\0" + VALUE "LegalCopyright", "Public Domain\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "adddsn.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite3 " SQLITE_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqlite.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Manifest +// + +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "adddsn.manifest" diff --git a/lang/sql/odbc/blobtoxy.c b/lang/sql/odbc/blobtoxy.c index 638bb2e8..09f1d5aa 100644 --- a/lang/sql/odbc/blobtoxy.c +++ b/lang/sql/odbc/blobtoxy.c @@ -1,12 +1,12 @@ -/* +/** * @file blobtoxy.c * SQLite extension module for read-only BLOB to X/Y mapping * using SQLite 3.3.x virtual table API plus some useful * scalar and aggregate functions. * - * $Id: blobtoxy.c,v 1.19 2011/08/21 10:11:24 chw Exp chw $ + * $Id: blobtoxy.c,v 1.22 2013/01/11 12:19:55 chw Exp chw $ * - * Copyright (c) 2007-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2007-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a @@ -30,71 +30,71 @@ * CREATE VIRTUAL TABLE t1 * USING blobtoxy (t, key, data, short_le, x_scale, x_offset, * y_scale, y_offset, "foo, bar"); - * CREATE VIRTUAL TABLE t2 + * <br>CREATE VIRTUAL TABLE t2 * USING blobtoxy (t, key, data, uchar, null, null, null, null, 'bar'); - * CREATE VIRTUAL TABLE t3 + * <br>CREATE VIRTUAL TABLE t3 * USING blobtoxy (t, key, data, int_be, 10.0, null, 10.0, null, "foo"); - * CREATE VIRTUAL TABLE t4 + * <br>CREATE VIRTUAL TABLE t4 * USING blobtoxy (t, key, data, float, null, -10, null, 10); * * Arguments to "blobtoxy" module: * - * 0. master table name (required). - * 1. key column in master table (required). - * 2. blob column in master table (required). - * 3. type code (optional), defaults to "char". + * 0. master table name (required).<br> + * 1. key column in master table (required).<br> + * 2. blob column in master table (required).<br> + * 3. type code (optional), defaults to "char".<br> * 4. X scale column in master table (optional), * may be specified as integer or float constant, too, * to explicitely omit scale, use an empty string ('') - * or null. + * or null.<br> * 5. X offset column in master table (optional), * may be specified as integer or float constant, too, * to explicitely omit offset, use an empty string ('') - * or null. - * 6. Y scale column in master table (optional), see point 4. - * 7. Y offset column in master table (optional), see point 5. + * or null.<br> + * 6. Y scale column in master table (optional), see point 4.<br> + * 7. Y offset column in master table (optional), see point 5.<br> * 8. other columns of the master table to appear in the * result set (optional), must be specified as a * single or double quoted string with comma * separated column names as a sequence of named * columns as it would be written in a SELECT - * statement. + * statement.<br> * 9. X start index (optional), specified as integer - * in type specific blob units, zero based. + * in type specific blob units, zero based.<br> * 10. X length (optional), specified as integer, - * number of blob units (= number of rows). + * number of blob units (= number of rows).<br> * * Supported data types: * - * "char" -> BLOB is a signed char array - * "uchar" -> BLOB is an unsigned char array - * "short_le" -> BLOB is a short array little endian - * "short_be" -> BLOB is a short array big endian - * "ushort_le" -> BLOB is an unsigned short array little endian - * "ushort_be" -> BLOB is an unsigned short array big endian - * "int_le" -> BLOB is a int array little endian - * "int_be" -> BLOB is a int array big endian - * "uint_le" -> BLOB is an unsigned int array little endian - * "uint_be" -> BLOB is an unsigned int array big endian - * "bigint_le" -> BLOB is an large integer array little endian - * "bigint_be" -> BLOB is an large integer array big endian - * "float" -> BLOB is a float array - * "double" -> BLOB is a double array + * "char" -> BLOB is a signed char array<br> + * "uchar" -> BLOB is an unsigned char array<br> + * "short_le" -> BLOB is a short array little endian<br> + * "short_be" -> BLOB is a short array big endian<br> + * "ushort_le" -> BLOB is an unsigned short array little endian<br> + * "ushort_be" -> BLOB is an unsigned short array big endian<br> + * "int_le" -> BLOB is a int array little endian<br> + * "int_be" -> BLOB is a int array big endian<br> + * "uint_le" -> BLOB is an unsigned int array little endian<br> + * "uint_be" -> BLOB is an unsigned int array big endian<br> + * "bigint_le" -> BLOB is an large integer array little endian<br> + * "bigint_be" -> BLOB is an large integer array big endian<br> + * "float" -> BLOB is a float array<br> + * "double" -> BLOB is a double array<br> * * Columns of "blobtoxy" mapped virtual table: * - * "key" Key column for JOINing with master table - * "x" index within BLOB. + * "key" Key column for JOINing with master table<br> + * "x" index within BLOB.<br> * This value is optionally translated by * the "x_scale" and "x_offset" columns * i.e. x' = x * x_scale + x_offset, yielding - * a floating point result. - * "y" BLOB's value at "x"-unscaled-index + * a floating point result.<br> + * "y" BLOB's value at "x"-unscaled-index<br> * This value is optionally translated by * the "y_scale" and "y_offset" columns * i.e. y' = y * y_scale + y_offset, yielding - * a floating point result. - * ... Other columns, see above + * a floating point result.<br> + * ... Other columns, see above<br> * * If the "key" field of the master table is an integer data * type, it is used as the ROWID of the mapped virtual table. @@ -105,43 +105,43 @@ * * Scalar context: * - * svg_path_from_blob(data, type, x_scale, x_offset, y_scale, y_offset) - * tk_path_from_blob(data, type, x_scale, x_offset, y_scale, y_offset) - * blt_vec_(x|y)(data, type, x_scale, x_offset, y_scale, y_offset) + * svg_path_from_blob(data, type, x_scale, x_offset, y_scale, y_offset)<br> + * tk_path_from_blob(data, type, x_scale, x_offset, y_scale, y_offset)<br> + * blt_vec_(x|y)(data, type, x_scale, x_offset, y_scale, y_offset)<br> * tk3d_path_from_blob(data, type, x_scale, x_offset, y_scale, y_offset, - * z_value, z_scale, z_offset) + * z_value, z_scale, z_offset)<br> * * Like BLOB to X/Y mapping but produces SVG or Tk Canvas * path/polyline as a string, e.g. * - * SVG: "M 1 1 L 2 2 L 3 7 L 4 1 - * Tk Canvas: "1 1 2 2 3 7 4 1" - * BLT Vector X: "1 2 3 4" - * BLT Vector Y: "1 2 7 1" - * Tk 3D Canvas: "1 1 0 2 2 0 3 7 0 4 1 0" + * SVG: "M 1 1 L 2 2 L 3 7 L 4 1<br> + * Tk Canvas: "1 1 2 2 3 7 4 1"<br> + * BLT Vector X: "1 2 3 4"<br> + * BLT Vector Y: "1 2 7 1"<br> + * Tk 3D Canvas: "1 1 0 2 2 0 3 7 0 4 1 0"<br> * * Arguments: * * 0. blob data (required); this parameter is always * interpreted as blob. It must contain at least * two elements, otherwise the function's result - * is NULL to indicate that nothing need be drawn - * 1. type code (optional), defaults to "char" - * 2. X scale (optional), see above - * 3. X offset (optional), see above - * 4. Y scale (optional), see above - * 5. Y offset (optional), see above - * 6. Z value (optional) - * 8. Z scale (optional) - * 9. Z offset (optional) + * is NULL to indicate that nothing need be drawn<br> + * 1. type code (optional), defaults to "char"<br> + * 2. X scale (optional), see above<br> + * 3. X offset (optional), see above<br> + * 4. Y scale (optional), see above<br> + * 5. Y offset (optional), see above<br> + * 6. Z value (optional)<br> + * 8. Z scale (optional)<br> + * 9. Z offset (optional)<br> * * Aggregate context: * - * svg_path(xdata, ydata, x_scale, x_offset, y_scale, y_offset) - * tk_path(xdata, ydata, x_scale, x_offset, y_scale, y_offset) - * blt_vec(data, scale, offset) + * svg_path(xdata, ydata, x_scale, x_offset, y_scale, y_offset)<br> + * tk_path(xdata, ydata, x_scale, x_offset, y_scale, y_offset)<br> + * blt_vec(data, scale, offset)<br> * tk3d_path(xdata, ydata, x_scale, x_offset, y_scale, y_offset, - * zdata, z_scale, z_offset) + * zdata, z_scale, z_offset)<br> * * Same behaviour except that xdata/ydata/data/zdata are interpreted * directly as numeric values. @@ -153,20 +153,20 @@ * * Works somewhat like substr, e.g. * - * select quote(subblob(X'0A0B0C0D0E0F0001',2,2,1,3)) + * select quote(subblob(X'0A0B0C0D0E0F0001',2,2,1,3))<br> * -> X'0B0F' * * Arguments: * * 0. blob data (required); this parameter is always - * interpreted as blob. + * interpreted as blob.<br> * 1. start offset (required) in bytes as in substr - * function (1-based, negative offsets count from end) - * 2. length (required) in bytes to be copied + * function (1-based, negative offsets count from end)<br> + * 2. length (required) in bytes to be copied<br> * 3. size (optional) of items in bytes to be copied - * in combination with skip argument + * in combination with skip argument<br> * 4. skip (optional) in bytes after one item of - * size argument has been copied + * size argument has been copied<br> * * * Exported SQLite function rownumber @@ -178,8 +178,8 @@ * must be provided to this function in order to satisfy * some needs of the SQLite3 C API, e.g. * - * rownumber(0), rownumber('foo') right - * rownumber(column_name) wrong, will yield always 0 + * rownumber(0), rownumber('foo') right<br> + * rownumber(column_name) wrong, will yield always 0<br> * */ @@ -187,7 +187,7 @@ #include <sqlite3.h> #else #include <sqlite3ext.h> -SQLITE_EXTENSION_INIT1 +static SQLITE_EXTENSION_INIT1 #endif #include <stdlib.h> @@ -219,50 +219,68 @@ SQLITE_EXTENSION_INIT1 #define TYPE_FLOAT TYPE_CODE(12, float) #define TYPE_DOUBLE TYPE_CODE(13, double) +/** + * @typedef b2xy_table + * @struct b2xy_table + * Structure to describe a virtual table. + */ + typedef struct b2xy_table { - sqlite3_vtab base; /* SQLite's base virtual table struct */ - sqlite3 *db; /* Open database */ - char *master_table; /* Table where to fetch BLOB from */ - char *fq_master_table; /* Fully qualified master_table */ - char *key_column; /* Name of key column */ - char *blob_column; /* Name of BLOB column */ - char *x_scale_column; /* Name of column giving X scale or NULL */ - char *x_offset_column; /* Name of column giving X offset or NULL */ - char *y_scale_column; /* Name of column giving Y scale or NULL */ - char *y_offset_column; /* Name of column giving Y offset or NULL */ - char *other_columns; /* Other columns or empty string */ - int type; /* Data type of BLOB */ - int do_x_sl; /* If true, apply X start/length */ - int x_start, x_length; /* X start/length */ - int argc; /* Number arguments from b2xy_create() call */ - char **argv; /* Argument vector from b2xy_create() call */ + sqlite3_vtab base; /**< SQLite's base virtual table struct */ + sqlite3 *db; /**< Open database */ + char *master_table; /**< Table where to fetch BLOB from */ + char *fq_master_table; /**< Fully qualified master_table */ + char *key_column; /**< Name of key column */ + char *blob_column; /**< Name of BLOB column */ + char *x_scale_column; /**< Name of column giving X scale or NULL */ + char *x_offset_column; /**< Name of column giving X offset or NULL */ + char *y_scale_column; /**< Name of column giving Y scale or NULL */ + char *y_offset_column; /**< Name of column giving Y offset or NULL */ + char *other_columns; /**< Other columns or empty string */ + int type; /**< Data type of BLOB */ + int do_x_sl; /**< If true, apply X start/length */ + int x_start, x_length; /**< X start/length */ + int argc; /**< Number args from b2xy_create() call */ + char **argv; /**< Argument vector from b2xy_create() call */ } b2xy_table; +/** + * @typedef b2xy_cursor + * @struct b2xy_cursor + * Structure to describe a cursor in the virtual table. + */ + typedef struct b2xy_cursor { - sqlite3_vtab_cursor base; /* SQLite's base cursor struct */ - b2xy_table *table; /* Link to table struct */ - sqlite3_stmt *select; /* Prepared SELECT statement or NULL */ - sqlite3_value *key; /* Value of current key */ - int fix_cols; /* Fixed number of columns of result set */ - int num_cols; /* Total number of columns of result set */ - char *val; /* Value of current BLOB */ - int val_len; /* Length of current BLOB */ - int x_scale_col; /* Column number of X scale or 0 */ - int x_offset_col; /* Column number of X offset or 0 */ - double x_scale, x_offset; /* Current X scale and offset */ - int y_scale_col; /* Column number of Y scale or 0 */ - int y_offset_col; /* Column number of Y offset or 0 */ - double y_scale, y_offset; /* Current X scale and offset */ - int do_x_scale; /* If true, use X scale and offset */ - int do_y_scale; /* If true, use Y scale and offset */ - int do_x_sl; /* If true, apply X start/length */ - int x_start, x_length; /* X start/length */ - int type; /* Data type of BLOB */ - int index; /* Current index in BLOB */ - int rowid_from_key; /* When true, ROWID used from key column */ - sqlite_int64 rowid; /* Current ROWID */ + sqlite3_vtab_cursor base; /**< SQLite's base cursor struct */ + b2xy_table *table; /**< Link to table struct */ + sqlite3_stmt *select; /**< Prepared SELECT statement or NULL */ + sqlite3_value *key; /**< Value of current key */ + int fix_cols; /**< Fixed number of columns of result set */ + int num_cols; /**< Total number of columns of result set */ + char *val; /**< Value of current BLOB */ + int val_len; /**< Length of current BLOB */ + int x_scale_col; /**< Column number of X scale or 0 */ + int x_offset_col; /**< Column number of X offset or 0 */ + double x_scale, x_offset; /**< Current X scale and offset */ + int y_scale_col; /**< Column number of Y scale or 0 */ + int y_offset_col; /**< Column number of Y offset or 0 */ + double y_scale, y_offset; /**< Current X scale and offset */ + int do_x_scale; /**< If true, use X scale and offset */ + int do_y_scale; /**< If true, use Y scale and offset */ + int do_x_sl; /**< If true, apply X start/length */ + int x_start, x_length; /**< X start/length */ + int type; /**< Data type of BLOB */ + int index; /**< Current index in BLOB */ + int rowid_from_key; /**< When true, ROWID used from key column */ + sqlite_int64 rowid; /**< Current ROWID */ } b2xy_cursor; +/** + * Map type string to type code. + * @param str type string, e.g. "char" + * @result type code, e.g. TYPE_CHAR + */ + static int string_to_type(const char *str) { @@ -311,6 +329,12 @@ string_to_type(const char *str) return 0; } +/** + * Destroy virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + static int b2xy_destroy(sqlite3_vtab *vtab) { @@ -320,6 +344,34 @@ b2xy_destroy(sqlite3_vtab *vtab) return SQLITE_OK; } +/** + * Create virtual table + * @param db SQLite database pointer + * @param userdata user specific pointer (unused) + * @param argc argument count + * @param argv argument vector + * @param vtabret pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + * + * Argument vector contains: + * + * argv[0] - module name<br> + * argv[1] - database name<br> + * argv[2] - table name (virtual table)<br> + * argv[3] - master table name (required)<br> + * argv[4] - key column (required)<br> + * argv[5] - blob column (required)<br> + * argv[6] - type code (optional)<br> + * argv[7] - X scale column (optional)<br> + * argv[8] - X offset column (optional)<br> + * argv[9] - Y scale column (optional)<br> + * argv[10] - Y offset column (optional)<br> + * argv[11] - other columns (optional)<br> + * argv[12] - X start (optional)<br> + * argv[13] - X length (optional)<br> + */ + static int b2xy_create(sqlite3 *db, void *userdata, int argc, const char * const *argv, sqlite3_vtab **vtabret, char **errp) @@ -329,22 +381,6 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, int i, size, type = TYPE_CHAR; int x_start = -1, x_length = 0; - /* - * argv[0] - module name - * argv[1] - database name - * argv[2] - table name (virtual table) - * argv[3] - master table name (required) - * argv[4] - key column (required) - * argv[5] - blob column (required) - * argv[6] - type code (optional) - * argv[7] - X scale column (optional) - * argv[8] - X offset column (optional) - * argv[9] - Y scale column (optional) - * argv[10] - Y offset column (optional) - * argv[11] - other columns (optional) - * argv[12] - X start (optional) - * argv[13] - X length (optional) - */ if (argc < 6) { *errp = sqlite3_mprintf("need at least 3 arguments"); return SQLITE_ERROR; @@ -357,7 +393,7 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, } } if (argc > 11) { - if (argv[11][0] != '"' && argv[11][0] != '\'') { + if ((argv[11][0] != '"') && (argv[11][0] != '\'')) { *errp = sqlite3_mprintf("other columns must be quoted"); return SQLITE_ERROR; } @@ -366,7 +402,7 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, char *endp = 0; x_start = strtol(argv[12], &endp, 10); - if (endp == argv[12] || (endp && endp[0] != '\0')) { + if ((endp == argv[12]) || (endp && (endp[0] != '\0'))) { *errp = sqlite3_mprintf("X start index must be integer"); return SQLITE_ERROR; } @@ -379,7 +415,7 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, char *endp = 0; x_length = strtol(argv[13], &endp, 10); - if (endp == argv[13] || (endp && endp[0] != '\0')) { + if ((endp == argv[13]) || (endp && (endp[0] != '\0'))) { *errp = sqlite3_mprintf("X length must be integer"); return SQLITE_ERROR; } @@ -433,25 +469,25 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, } bt->key_column = bt->argv[4]; bt->blob_column = bt->argv[5]; - if (bt->argc > 7 && bt->argv[7][0]) { + if ((bt->argc > 7) && bt->argv[7][0]) { bt->x_scale_column = bt->argv[7]; if (strcasecmp(bt->x_scale_column, "null") == 0) { bt->x_scale_column = 0; } } - if (bt->argc > 8 && bt->argv[8][0]) { + if ((bt->argc > 8) && bt->argv[8][0]) { bt->x_offset_column = bt->argv[8]; if (strcasecmp(bt->x_offset_column, "null") == 0) { bt->x_offset_column = 0; } } - if (bt->argc > 9 && bt->argv[9][0]) { + if ((bt->argc > 9) && bt->argv[9][0]) { bt->y_scale_column = bt->argv[9]; if (strcasecmp(bt->y_scale_column, "null") == 0) { bt->y_scale_column = 0; } } - if (bt->argc > 10 && bt->argv[10][0]) { + if ((bt->argc > 10) && bt->argv[10][0]) { bt->y_offset_column = bt->argv[10]; if (strcasecmp(bt->y_offset_column, "null") == 0) { bt->y_offset_column = 0; @@ -462,7 +498,7 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, p[0] = ','; bt->other_columns = p; p += strlen(p) - 1; - if (*p == '"' || *p == '\'') { + if ((*p == '"') || (*p == '\'')) { *p = '\0'; } } else { @@ -470,13 +506,13 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, } /* find out types of key and x/y columns */ if (bt->x_scale_column || bt->x_offset_column || - bt->type == TYPE_FLOAT || bt->type == TYPE_DOUBLE) { + (bt->type == TYPE_FLOAT) || (bt->type == TYPE_DOUBLE)) { x_type = " DOUBLE"; } else { x_type = " INTEGER"; } if (bt->y_scale_column || bt->y_offset_column || - bt->type == TYPE_FLOAT || bt->type == TYPE_DOUBLE) { + (bt->type == TYPE_FLOAT) || (bt->type == TYPE_DOUBLE)) { y_type = " DOUBLE"; } else { y_type = " INTEGER"; @@ -491,9 +527,9 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, rc = sqlite3_get_table(db, p, &rows, &nrows, &ncols, 0); sqlite3_free(p); if (rc == SQLITE_OK) { - for (i = 1; ncols >= 3 && i <= nrows; i++) { + for (i = 1; (ncols >= 3) && (i <= nrows); i++) { p = rows[i * ncols + 1]; - if (p && strcasecmp(bt->key_column, p) == 0) { + if (p && (strcasecmp(bt->key_column, p) == 0)) { key_type = sqlite3_mprintf(" %s", rows[i * ncols + 2]); break; } @@ -518,7 +554,7 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, rc = sqlite3_prepare(db, p, -1, &stmt, 0); #endif sqlite3_free(p); - if (rc == SQLITE_OK && stmt) { + if ((rc == SQLITE_OK) && stmt) { sqlite3_step(stmt); for (i = 0; i < sqlite3_column_count(stmt); i++) { p = sqlite3_mprintf("%s%s\"%s\" %s", @@ -567,6 +603,13 @@ b2xy_create(sqlite3 *db, void *userdata, int argc, return rc; } +/** + * Open virtual table and return cursor. + * @param vtab virtual table pointer + * @param curret pointer receiving cursor pointer + * @result SQLite error code + */ + static int b2xy_open(sqlite3_vtab *vtab, sqlite3_vtab_cursor **curret) { @@ -588,6 +631,12 @@ b2xy_open(sqlite3_vtab *vtab, sqlite3_vtab_cursor **curret) return rc; } +/** + * Close virtual table cursor. + * @param cur cursor pointer + * @result SQLite error code + */ + static int b2xy_close(sqlite3_vtab_cursor *cur) { @@ -598,6 +647,14 @@ b2xy_close(sqlite3_vtab_cursor *cur) return SQLITE_OK; } +/** + * Return column data of virtual table. + * @param cur virtual table cursor + * @param ctx SQLite function context + * @param i column index + * @result SQLite error code + */ + static int b2xy_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i) { @@ -618,7 +675,8 @@ b2xy_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i) } break; case 2: - if (!bc->val || (bc->index + 1) * TYPE_SIZE(bc->type) > bc->val_len) { + if (!bc->val || + ((bc->index + 1) * TYPE_SIZE(bc->type) > bc->val_len)) { goto put_null; } p = bc->val + bc->index * TYPE_SIZE(bc->type); @@ -762,7 +820,7 @@ b2xy_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i) break; default: i += bc->fix_cols - 3; - if (i < 0 || i >= bc->num_cols) { + if ((i < 0) || (i >= bc->num_cols)) { sqlite3_result_null(ctx); } else { sqlite3_result_value(ctx, sqlite3_column_value(bc->select, i)); @@ -772,6 +830,13 @@ b2xy_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i) return SQLITE_OK; } +/** + * Return current rowid of virtual table cursor + * @param cur virtual table cursor + * @param rowidp value buffer to receive current rowid + * @result SQLite error code + */ + static int b2xy_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *rowidp) { @@ -781,6 +846,12 @@ b2xy_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *rowidp) return SQLITE_OK; } +/** + * Return end of table state of virtual table cursor + * @param cur virtual table cursor + * @result true/false + */ + static int b2xy_eof(sqlite3_vtab_cursor *cur) { @@ -789,6 +860,12 @@ b2xy_eof(sqlite3_vtab_cursor *cur) return bc->select ? 0 : 1; } +/** + * Retrieve next row from virtual table cursor + * @param cur virtual table cursor + * @result SQLite error code + */ + static int b2xy_next(sqlite3_vtab_cursor *cur) { @@ -838,7 +915,7 @@ refetch: } } if (!(bc->do_x_sl && bc->x_length) && - ((bc->index + 1) * TYPE_SIZE(bc->type) > bc->val_len)) { + (((bc->index + 1) * TYPE_SIZE(bc->type) > bc->val_len))) { goto refetch; } bc->key = sqlite3_column_value(bc->select, 0); @@ -875,6 +952,16 @@ refetch: return SQLITE_OK; } +/** + * Filter function for virtual table. + * @param cur virtual table cursor + * @param idxNum used for expression (<, =, >, etc.) + * @param idxStr optional order by clause + * @param argc number arguments (0 or 1) + * @param argv argument (nothing or RHS of filter expression) + * @result SQLite error code + */ + static int b2xy_filter(sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) @@ -943,7 +1030,7 @@ b2xy_filter(sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, return SQLITE_NOMEM; } query = tmp; - if (idxNum && argc > 0) { + if (idxNum && (argc > 0)) { switch (idxNum) { case SQLITE_INDEX_CONSTRAINT_EQ: op = "="; @@ -1001,6 +1088,14 @@ b2xy_filter(sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, return (rc == SQLITE_OK) ? b2xy_next(cur) : rc; } +/** + * Determines information for filter function + * according to constraints. + * @param tab virtual table + * @param info index/constraint iinformation + * @result SQLite error code + */ + static int b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) { @@ -1020,8 +1115,8 @@ b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) */ for (i = 0; i < info->nConstraint; ++i) { if (info->aConstraint[i].usable) { - if (info->aConstraint[i].iColumn == 0 && - info->aConstraint[i].op != 0) { + if ((info->aConstraint[i].iColumn == 0) && + (info->aConstraint[i].op != 0)) { info->idxNum = info->aConstraint[i].op; info->aConstraintUsage[i].argvIndex = 1; info->aConstraintUsage[i].omit = 1; @@ -1041,7 +1136,7 @@ b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) if (info->aOrderBy[i].iColumn == 0) { key_order = info->aOrderBy[i].desc ? -1 : 1; consumed++; - } else if (info->aOrderBy[i].iColumn == 1 && + } else if ((info->aOrderBy[i].iColumn == 1) && !info->aOrderBy[i].desc) { consumed++; } @@ -1049,7 +1144,7 @@ b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) if (consumed) { /* check for other ORDER BY columns */ for (i = 0; i < info->nOrderBy; i++) { - if (info->aOrderBy[i].iColumn == 1 && + if ((info->aOrderBy[i].iColumn == 1) && info->aOrderBy[i].desc) { consumed = 0; } else if (info->aOrderBy[i].iColumn > 1) { @@ -1060,7 +1155,7 @@ b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) if (consumed && key_order) { info->idxStr = sqlite3_mprintf("ORDER BY \"%s\" %s", bt->key_column, - key_order < 0 ? "DESC" : "ASC"); + (key_order < 0) ? "DESC" : "ASC"); info->needToFreeIdxStr = 1; } info->orderByConsumed = consumed; @@ -1068,16 +1163,24 @@ b2xy_bestindex(sqlite3_vtab *tab, sqlite3_index_info *info) } #if (SQLITE_VERSION_NUMBER > 3004000) + +/** + * Rename virtual table. + * @param newname new name for table + * @result SQLite error code + */ + static int b2xy_rename(sqlite3_vtab *tab, const char *newname) { return SQLITE_OK; } + #endif static const sqlite3_module b2xy_module = { 1, /* iVersion */ - b2xy_create, /* xCreate */ + b2xy_create, /* xCreate */ b2xy_create, /* xConnect */ b2xy_bestindex, /* xBestIndex */ b2xy_destroy, /* xDisconnect */ @@ -1100,17 +1203,30 @@ static const sqlite3_module b2xy_module = { #endif }; +/** + * @typedef strbuf + * @struct strbuf + * Internal dynamic string buffer. + */ + typedef struct { - int max, idx; - char *str; + int max; /**< maximum capacity */ + int idx; /**< current index */ + char *str; /**< string buffer */ } strbuf; +/** + * Initialize dynamic string buffer with capacity 1024. + * @param sb pointer to string buffer + * @result SQLite error code + */ + static int init_strbuf(strbuf *sb) { int n = 1024; - if (sb->max <= 0 || !sb->str) { + if ((sb->max <= 0) || !sb->str) { sb->str = sqlite3_malloc(n); if (!sb->str) { return SQLITE_NOMEM; @@ -1121,13 +1237,19 @@ init_strbuf(strbuf *sb) return SQLITE_OK; } +/** + * Expand or initialize dynamic string buffer. + * @param sb pointer to string buffer + * @result SQLite error code + */ + static int expand_strbuf(strbuf *sb) { int n; char *str; - if (sb->max <= 0 || !sb->str) { + if ((sb->max <= 0) || !sb->str) { return init_strbuf(sb); } n = sb->max * 2; @@ -1140,6 +1262,11 @@ expand_strbuf(strbuf *sb) return SQLITE_OK; } +/** + * Free resources of dynamic string buffer. + * @param sb pointer to string buffer + */ + static void drop_strbuf(strbuf *sb) { @@ -1150,6 +1277,13 @@ drop_strbuf(strbuf *sb) sb->max = 0; } +/** + * Format printf-like into dynamic string buffer. + * @param sb pointer to string buffer + * @param fmt printf-like format string + * @result SQLite error code + */ + static int print_strbuf(strbuf *sb, const char *fmt, ...) { @@ -1166,7 +1300,7 @@ print_strbuf(strbuf *sb, const char *fmt, ...) } rc = SQLITE_NOMEM; n = vsnprintf(sb->str + sb->idx, sb->max - sb->idx, fmt, ap); - if (n >= 0 && (sb->idx + n) < (sb->max - 1)) { + if ((n >= 0) && ((sb->idx + n) < (sb->max - 1))) { sb->idx += n; rc = SQLITE_OK; break; @@ -1183,6 +1317,25 @@ print_strbuf(strbuf *sb, const char *fmt, ...) #define PATH_MODE_BLT ((void *) 4) #define PATH_MODE_TK3D ((void *) 5) +/** + * Make path/polyline from blob. + * @param ctx SQLite function context + * @param nargs number arguments + * @param args arguments + * + * Arguments: + * + * args[0] - blob data (required)<br> + * args[1] - type (optional, default "char")<br> + * args[2] - X scale (optional)<br> + * args[3] - X offset (optional)<br> + * args[4] - Y scale (optional)<br> + * args[5] - Y offset (optional)<br> + * args[6] - Z value (optional)<br> + * args[7] - Z scale (optional)<br> + * args[8] - Z offset (optional)<br> + */ + static void common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1193,17 +1346,6 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) double x_scale, x_offset, y_scale, y_offset, z0, z_scale, z_offset; strbuf sb; - /* - * args[0] - blob data (required) - * args[1] - type (optional) - * args[2] - X scale (optional) - * args[3] - X offset (optional) - * args[4] - Y scale (optional) - * args[5] - Y offset (optional) - * args[6] - Z value (optional) - * args[7] - Z scale (optional) - * args[8] - Z offset (optional) - */ if (nargs < 1) { sqlite3_result_error(ctx, "need at least 1 argument", -1); return; @@ -1218,8 +1360,8 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) data = (char *) sqlite3_value_blob(args[0]); size = sqlite3_value_bytes(args[0]) / TYPE_SIZE(type); if (!data || - (mode != PATH_MODE_BLT_X && mode != PATH_MODE_BLT_Y && size < 2) || - size < 1) { + ((mode != PATH_MODE_BLT_X) && (mode != PATH_MODE_BLT_Y) && + (size < 2)) || (size < 1)) { goto nullorempty; } x_scale = 1; @@ -1245,14 +1387,14 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) z0 = 0; z_scale = 1; z_offset = 0; - if (mode == PATH_MODE_TK3D && nargs > 6) { + if ((mode == PATH_MODE_TK3D) && (nargs > 6)) { z0 = sqlite3_value_double(args[6]); } - if (mode == PATH_MODE_TK3D && nargs > 7) { + if ((mode == PATH_MODE_TK3D) && (nargs > 7)) { z_scale = sqlite3_value_double(args[7]); do_z_scale++; } - if (mode == PATH_MODE_TK3D && nargs > 8) { + if ((mode == PATH_MODE_TK3D) && (nargs > 8)) { z_offset = sqlite3_value_double(args[8]); do_z_scale++; } @@ -1318,7 +1460,7 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) if (do_y_scale) { y = y * y_scale + y_offset; } - if (mode == PATH_MODE_BLT_X || mode == PATH_MODE_BLT_Y) { + if ((mode == PATH_MODE_BLT_X) || (mode == PATH_MODE_BLT_Y)) { double v = (mode == PATH_MODE_BLT_X) ? x : y; if (print_strbuf(&sb, (i == 0) ? "%g" : " %g", v) != SQLITE_OK) { @@ -1327,11 +1469,11 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) } continue; } - if (mode == PATH_MODE_SVG && i == 0) { + if ((mode == PATH_MODE_SVG) && (i == 0)) { fmt = "M %g %g"; - } else if (mode == PATH_MODE_SVG && i == 1) { + } else if ((mode == PATH_MODE_SVG) && (i == 1)) { fmt = " L %g %g"; - } else if (mode == PATH_MODE_SVG && sb.idx >= linebreak) { + } else if ((mode == PATH_MODE_SVG) && (sb.idx >= linebreak)) { fmt = "\nL %g %g"; linebreak = sb.idx + 100; } else if (i == 0) { @@ -1350,19 +1492,47 @@ common_path_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) return; } nullorempty: - if (mode == PATH_MODE_BLT_X || mode == PATH_MODE_BLT_Y) { + if ((mode == PATH_MODE_BLT_X) || (mode == PATH_MODE_BLT_Y)) { sqlite3_result_text(ctx, "", 0, SQLITE_STATIC); } else { sqlite3_result_null(ctx); } } +/** + * @typedef path_aggctx + * @struct path_aggctx + * Internal aggregate context for path/polyline function. + */ + typedef struct { - int init, count, linebreak; - void *mode; - strbuf sb; + int init; /**< init flag, true when initialized */ + int count; /**< counts formatted elements */ + int linebreak; /**< when to add newline to output */ + void *mode; /**< mode, see PATH_* defines */ + strbuf sb; /**< string buffer for result */ } path_aggctx; +/** + * Path/polyline step callback for "tk_path", "svg_path", and + * "tk3d_path" aggregate functions. + * @param ctx SQLite function context + * @param nargs number arguments + * @param args arguments + * + * Arguments: + * + * args[0] - X value (required)<br> + * args[1] - Y value (required)<br> + * args[2] - X scale (optional)<br> + * args[3] - X offset (optional)<br> + * args[4] - Y scale (optional)<br> + * args[5] - Y offset (optional)<br> + * args[6] - Z value (optional)<br> + * args[7] - Z scale (optional)<br> + * args[8] - Z offset (optional)<br> + */ + static void common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1372,17 +1542,6 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) double x, y, z = 0; double x_scale, y_scale, x_offset, y_offset, z_scale, z_offset; - /* - * args[0] - X value (required) - * args[1] - Y value (required) - * args[2] - X scale (optional) - * args[3] - X offset (optional) - * args[4] - Y scale (optional) - * args[5] - Y offset (optional) - * args[6] - Z value (optional) - * args[7] - Z scale (optional) - * args[8] - Z offset (optional) - */ if (nargs < 2) { return; } @@ -1397,11 +1556,11 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) pag->init = 1; } type = sqlite3_value_type(args[0]); - if (type != SQLITE_INTEGER && type != SQLITE_FLOAT) { + if ((type != SQLITE_INTEGER) && (type != SQLITE_FLOAT)) { return; } type = sqlite3_value_type(args[1]); - if (type != SQLITE_INTEGER && type != SQLITE_FLOAT) { + if ((type != SQLITE_INTEGER) && (type != SQLITE_FLOAT)) { return; } x = sqlite3_value_double(args[0]); @@ -1410,13 +1569,13 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) x_offset = 0; if (nargs > 2) { type = sqlite3_value_type(args[2]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { x_scale = sqlite3_value_double(args[2]); } } if (nargs > 3) { type = sqlite3_value_type(args[3]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { x_offset = sqlite3_value_double(args[3]); } } @@ -1424,29 +1583,29 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) y_offset = 0; if (nargs > 4) { type = sqlite3_value_type(args[4]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { y_scale = sqlite3_value_double(args[4]); } } if (nargs > 5) { type = sqlite3_value_type(args[5]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { y_offset = sqlite3_value_double(args[5]); } } z_scale = 1; z_offset = 0; - if (pag->mode == PATH_MODE_TK3D && nargs > 6) { + if ((pag->mode == PATH_MODE_TK3D) && (nargs > 6)) { z = sqlite3_value_double(args[6]); if (nargs > 7) { type = sqlite3_value_type(args[7]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { z_scale = sqlite3_value_double(args[7]); } } if (nargs > 8) { type = sqlite3_value_type(args[8]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { z_offset = sqlite3_value_double(args[8]); } } @@ -1454,11 +1613,12 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) } x = x * x_scale + x_offset; y = y * y_scale + y_offset; - if (pag->mode == PATH_MODE_SVG && pag->count == 0) { + if ((pag->mode == PATH_MODE_SVG) && (pag->count == 0)) { fmt = "M %g %g"; - } else if (pag->mode == PATH_MODE_SVG && pag->count == 1) { + } else if ((pag->mode == PATH_MODE_SVG) && (pag->count == 1)) { fmt = " L %g %g"; - } else if (pag->mode == PATH_MODE_SVG && pag->sb.idx >= pag->linebreak) { + } else if ((pag->mode == PATH_MODE_SVG) && + (pag->sb.idx >= pag->linebreak)) { fmt = "\nL %g %g"; pag->linebreak = pag->sb.idx + 100; } else if (pag->count == 0) { @@ -1474,13 +1634,18 @@ common_path_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) } } +/** + * Path/polyline finalizer. + * @param ctx SQLite function context + */ + static void common_path_finalize(sqlite3_context *ctx) { path_aggctx *pag = sqlite3_aggregate_context(ctx, sizeof (*pag)); if (pag->init) { - if (pag->count > 1 || pag->mode == PATH_MODE_BLT) { + if ((pag->count > 1) || (pag->mode == PATH_MODE_BLT)) { sqlite3_result_text(ctx, pag->sb.str, pag->sb.idx, sqlite3_free); pag->sb.str = 0; pag->init = 0; @@ -1495,6 +1660,19 @@ common_path_finalize(sqlite3_context *ctx) } } +/** + * Path/polyline step callback for "blt_vec" aggregate functions. + * @param ctx SQLite function context + * @param nargs number arguments + * @param args arguments + * + * Arguments: + * + * args[0] - value (required)<br> + * args[1] - scale (optional)<br> + * args[2] - offset (optional)<br> + */ + static void blt_vec_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1502,11 +1680,6 @@ blt_vec_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) int type; double v, scale, offset; - /* - * args[0] - value (required) - * args[1] - scale (optional) - * args[2] - offset (optional) - */ if (nargs < 1) { return; } @@ -1520,7 +1693,7 @@ blt_vec_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) pag->init = 1; } type = sqlite3_value_type(args[0]); - if (type != SQLITE_INTEGER && type != SQLITE_FLOAT) { + if ((type != SQLITE_INTEGER) && (type != SQLITE_FLOAT)) { return; } v = sqlite3_value_double(args[0]); @@ -1528,13 +1701,13 @@ blt_vec_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) offset = 0; if (nargs > 1) { type = sqlite3_value_type(args[1]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { scale = sqlite3_value_double(args[2]); } } if (nargs > 2) { type = sqlite3_value_type(args[2]); - if (type == SQLITE_INTEGER || type == SQLITE_FLOAT) { + if ((type == SQLITE_INTEGER) || (type == SQLITE_FLOAT)) { offset = sqlite3_value_double(args[3]); } } @@ -1548,6 +1721,13 @@ blt_vec_step(sqlite3_context *ctx, int nargs, sqlite3_value **args) } } +/** + * "subblob" function similar to "substr". + * @param ctx SQLite function context + * @param nargs number arguments + * @param args arguments + */ + static void subblob_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1561,7 +1741,7 @@ subblob_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) } indata = (char *) sqlite3_value_blob(args[0]); insize = sqlite3_value_bytes(args[0]); - if (!indata || insize <= 0) { + if (!indata || (insize <= 0)) { isnull: sqlite3_result_null(ctx); return; @@ -1587,7 +1767,7 @@ isnull: } if (nargs > 3) { itemsize = sqlite3_value_int(args[3]); - if (itemsize <= 0 || itemsize > outsize) { + if ((itemsize <= 0) || (itemsize > outsize)) { goto isnull; } } @@ -1624,18 +1804,31 @@ isnull: sqlite3_free(outdata); } +/** + * @typedef rownumber_ctx + * @struct rownumber_ctx + * SQLite context structure for "rownumber" function. + */ + typedef struct { - sqlite3_context *ctx; - sqlite3_value *value; - sqlite_int64 count; + sqlite3_context *ctx; /**< SQLite context */ + sqlite3_value *value; /**< SQLite value for this context */ + sqlite_int64 count; /**< Counter giving row number */ } rownumber_ctx; +/** + * "rownumber" function. + * @param ctx SQLite function context + * @param nargs number arguments + * @param args arguments + */ + static void rownumber_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { rownumber_ctx *rn = sqlite3_get_auxdata(ctx, 0); - if (!rn || rn->ctx != ctx || rn->value != args[0]) { + if (!rn || (rn->ctx != ctx) || (rn->value != args[0])) { rn = sqlite3_malloc(sizeof (*rn)); if (rn) { rn->ctx = ctx; @@ -1649,6 +1842,13 @@ rownumber_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) sqlite3_result_int64(ctx, rn ? rn->count : 0); } +/** + * Module initializer creating SQLite functions and + * modules. + * @param db SQLite database pointer + * @result SQLite error code + */ + #ifndef STANDALONE static #endif @@ -1681,6 +1881,15 @@ b2xy_init(sqlite3 *db) } #ifndef STANDALONE + +/** + * Initializer for SQLite extension load mechanism. + * @param db SQLite database pointer + * @param errmsg pointer receiving error message + * @param api SQLite API routines + * @result SQLite error code + */ + int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) @@ -1688,4 +1897,5 @@ sqlite3_extension_init(sqlite3 *db, char **errmsg, SQLITE_EXTENSION_INIT2(api); return b2xy_init(db); } + #endif diff --git a/lang/sql/odbc/blobtoxy.rc b/lang/sql/odbc/blobtoxy.rc index ba9a72b9..91c6b5ca 100644 --- a/lang/sql/odbc/blobtoxy.rc +++ b/lang/sql/odbc/blobtoxy.rc @@ -25,7 +25,7 @@ BEGIN VALUE "FileDescription", "SQLite3 Extension BLOB to X/Y\0" VALUE "FileVersion", VERSION "\0" VALUE "InternalName", "BLOBTOXY\0" - VALUE "LegalCopyright", "Copyright © 2007-2011 <chw@ch-werner.de>\0" + VALUE "LegalCopyright", "Copyright © 2007-2013 <chw@ch-werner.de>\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "SQLITE3_MOD_BLOBTOXY.DLL\0" VALUE "PrivateBuild", "\0" diff --git a/lang/sql/odbc/configure b/lang/sql/odbc/configure index b71e40c8..7e8692a8 100755 --- a/lang/sql/odbc/configure +++ b/lang/sql/odbc/configure @@ -463,7 +463,7 @@ ac_includes_default="\ # include <unistd.h> #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SED EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP CPP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SQLITE_INC SQLITE_LIB SQLITE_LIBVERSION SQLITE_ENCDEC SQLITE_ATOF SQLITE_MPRINTF SQLITE_TRACE SQLITE3_INC SQLITE3_A10N_C SQLITE3_A10N_O SQLITE3_A10N_FLAGS SQLITE3_COLUMNTABLENAME SQLITE3_OVERLOADFUNCTION SQLITE3_PREPARE_V2 SQLITE3_CLEARBINDINGS SQLITE3_CREATEMODULE_V2 SQLITE3_LOADEXTENSION SQLITE3_TABLECOLUMNMETADATA SQLITE3_VFS SQLITE3_PROFILE SQLITE3_STRNICMP EXT_BLOBTOXY EXT_IMPEXP LIB_TARGETS INST_TARGETS UNINST_TARGETS DRVINST_TARGETS DRVUNINST_TARGETS ODBC_FLAGS ODBC_LIB VER_INFO DRVDIR DL_OPTS DL_INITFINI SQLITE3_LIB SPEC_CHANGELOG_TS DEB_CHANGELOG_TS LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT SED EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP CPP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SQLITE_INC SQLITE_LIB SQLITE_LIBVERSION SQLITE_ENCDEC SQLITE_ATOF SQLITE_MPRINTF SQLITE_TRACE SQLITE3_INC SQLITE3_A10N_C SQLITE3_A10N_FLAGS SQLITE3_COLUMNTABLENAME SQLITE3_OVERLOADFUNCTION SQLITE3_PREPARE_V2 SQLITE3_CLEARBINDINGS SQLITE3_CREATEMODULE_V2 SQLITE3_LOADEXTENSION SQLITE3_TABLECOLUMNMETADATA SQLITE3_VFS SQLITE3_PROFILE SQLITE3_STRNICMP SQLITE3_CLOSE_V2 EXT_BLOBTOXY EXT_IMPEXP EXT_CSVTABLE EXT_ZIPFILE SQLITE4_INC SQLITE4_A10N_C SQLITE4_A10N_FLAGS XML2_FLAGS XML2_LIBS EXT_XPATH LIB_TARGETS INST_TARGETS UNINST_TARGETS DRVINST_TARGETS DRVUNINST_TARGETS ODBC_FLAGS ODBC_LIB VER_INFO DRVDIR DL_OPTS DL_INITFINI SQLITE3_LIB SQLITE3_A10N_O SQLITE4_A10N_O SPEC_CHANGELOG_TS DEB_CHANGELOG_TS LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -1037,8 +1037,9 @@ Optional Packages: include additional configurations [automatic] --with-sqlite=DIR use SQLite header/lib from DIR --with-sqlite3=DIR use SQLite3 hdr/lib from DIR + --with-sqlite4=DIR use SQLite4 hdr/src from DIR --with-odbc=DIR use ODBC header/libs from DIR - --with-dls dlopen SQLite3 lib + --with-dls dlopen SQLite3/SQLite4 lib Some influential environment variables: CC C compiler command @@ -3069,7 +3070,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 3072 "configure"' > conftest.$ac_ext + echo '#line 3073 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -4559,7 +4560,7 @@ fi # Provide some information about the compiler. -echo "$as_me:4562:" \ +echo "$as_me:4563:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 @@ -5626,11 +5627,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:5629: $lt_compile\"" >&5) + (eval echo "\"\$as_me:5630: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:5633: \$? = $ac_status" >&5 + echo "$as_me:5634: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -5894,11 +5895,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:5897: $lt_compile\"" >&5) + (eval echo "\"\$as_me:5898: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:5901: \$? = $ac_status" >&5 + echo "$as_me:5902: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -5998,11 +5999,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6001: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6002: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:6005: \$? = $ac_status" >&5 + echo "$as_me:6006: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7467,7 +7468,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 7470 "configure"' > conftest.$ac_ext + echo '#line 7471 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -8364,7 +8365,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 8367 "configure" +#line 8368 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -8464,7 +8465,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 8467 "configure" +#line 8468 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10811,11 +10812,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10814: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10815: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:10818: \$? = $ac_status" >&5 + echo "$as_me:10819: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -10915,11 +10916,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:10918: $lt_compile\"" >&5) + (eval echo "\"\$as_me:10919: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:10922: \$? = $ac_status" >&5 + echo "$as_me:10923: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -11451,7 +11452,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 11454 "configure"' > conftest.$ac_ext + echo '#line 11455 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -12509,11 +12510,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12512: $lt_compile\"" >&5) + (eval echo "\"\$as_me:12513: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:12516: \$? = $ac_status" >&5 + echo "$as_me:12517: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -12613,11 +12614,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12616: $lt_compile\"" >&5) + (eval echo "\"\$as_me:12617: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:12620: \$? = $ac_status" >&5 + echo "$as_me:12621: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -14062,7 +14063,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 14065 "configure"' > conftest.$ac_ext + echo '#line 14066 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -14844,11 +14845,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14847: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14848: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:14851: \$? = $ac_status" >&5 + echo "$as_me:14852: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -15112,11 +15113,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15115: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15116: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15119: \$? = $ac_status" >&5 + echo "$as_me:15120: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -15216,11 +15217,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15219: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15220: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15223: \$? = $ac_status" >&5 + echo "$as_me:15224: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16685,7 +16686,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 16688 "configure"' > conftest.$ac_ext + echo '#line 16689 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -18499,8 +18500,8 @@ CFLAGS=$saved_CFLAGS if test "$SQLITE_COMPILE" = "0" ; then - { echo "$as_me:$LINENO: WARNING: SQLite library too old" >&5 -echo "$as_me: WARNING: SQLite library too old" >&2;} + { echo "$as_me:$LINENO: WARNING: SQLite library too old, need 2.8.0 or later" >&5 +echo "$as_me: WARNING: SQLite library too old, need 2.8.0 or later" >&2;} else LIB_TARGETS="$LIB_TARGETS libsqliteodbc.la" INST_TARGETS="$INST_TARGETS install-2" @@ -18593,7 +18594,6 @@ fi - # test for availability of some sqlite3_*() funcs if test -n "$SQLITE3_LIBDIR" ; then @@ -18601,15 +18601,17 @@ if test -n "$SQLITE3_LIBDIR" ; then fi if test -n "$SQLITE3_A10N_C" ; then - SQLITE3_LOADEXTENSTION=1 + SQLITE3_LOADEXTENSION=1 SQLITE3_COLUMNTABLENAME=1 SQLITE3_OVERLOADFUNCTION=1 SQLITE3_PREPARE_V2=1 SQLITE3_CLEARBINDINGS=1 SQLITE3_CREATEMODULE_V2=1 - SQLITE3_TABLECOLUMNMETADATA=1 SQLITE3_VFS=1 + SQLITE3_TABLECOLUMNMETADATA=1 + SQLITE3_PROFILE=1 SQLITE3_STRNICMP=1 + SQLITE3_CLOSE_V2=1 else saved_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -I$SQLITE3_INC" @@ -19393,17 +19395,380 @@ else SQLITE3_STRNICMP=0 fi + echo "$as_me:$LINENO: checking for sqlite3_close_v2 in -lsqlite3" >&5 +echo $ECHO_N "checking for sqlite3_close_v2 in -lsqlite3... $ECHO_C" >&6 +if test "${ac_cv_lib_sqlite3_sqlite3_close_v2+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsqlite3 $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char sqlite3_close_v2 (); +int +main () +{ +sqlite3_close_v2 (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_sqlite3_sqlite3_close_v2=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_sqlite3_sqlite3_close_v2=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_sqlite3_sqlite3_close_v2" >&5 +echo "${ECHO_T}$ac_cv_lib_sqlite3_sqlite3_close_v2" >&6 +if test $ac_cv_lib_sqlite3_sqlite3_close_v2 = yes; then + SQLITE3_CLOSE_V2=1 +else + SQLITE3_CLOSE_V2=0 +fi + LIBS=$saved_LIBS CFLAGS=$saved_CFLAGS fi +######### +# Find zlib for zipfile module +# +LIBZ_OK=no +if test "$SQLITE3_LOADEXTENSION" = "1" ; then + echo "$as_me:$LINENO: checking for library containing zlibVersion" >&5 +echo $ECHO_N "checking for library containing zlibVersion... $ECHO_C" >&6 +if test "${ac_cv_search_zlibVersion+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_zlibVersion=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char zlibVersion (); +int +main () +{ +zlibVersion (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_zlibVersion="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_zlibVersion" = no; then + for ac_lib in z; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char zlibVersion (); +int +main () +{ +zlibVersion (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_zlibVersion="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_zlibVersion" >&5 +echo "${ECHO_T}$ac_cv_search_zlibVersion" >&6 +if test "$ac_cv_search_zlibVersion" != no; then + test "$ac_cv_search_zlibVersion" = "none required" || LIBS="$ac_cv_search_zlibVersion $LIBS" + +for ac_header in zlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + LIBZ_OK=yes +fi + +done + +fi + +fi + +######### +# Add extensions to build +# if test "$SQLITE3_LOADEXTENSION" = "1" ; then EXT_BLOBTOXY=libsqlite3_mod_blobtoxy.la EXT_IMPEXP=libsqlite3_mod_impexp.la - LIB_TARGETS="$LIB_TARGETS $EXT_BLOBTOXY $EXT_IMPEXP" + EXT_CSVTABLE=libsqlite3_mod_csvtable.la + EXT_ZIPFILE="" + LIB_TARGETS="$LIB_TARGETS $EXT_BLOBTOXY $EXT_IMPEXP $EXT_CSVTABLE" + if test "$LIBZ_OK" = "yes" ; then + EXT_ZIPFILE=libsqlite3_mod_zipfile.la + LIB_TARGETS="$LIB_TARGETS $EXT_ZIPFILE" + fi else EXT_BLOBTOXY="" EXT_IMPEXP="" + EXT_CSVTABLE="" + EXT_ZIPFILE="" fi @@ -19419,6 +19784,87 @@ fi + + + +########## +# Find SQLite4 header file and library +# + +# Check whether --with-sqlite4 or --without-sqlite4 was given. +if test "${with_sqlite4+set}" = set; then + withval="$with_sqlite4" + SQLITE4_DIR=$withval +fi; +if test -n "$SQLITE4_DIR" ; then + if test ! -d "$SQLITE4_DIR" ; then + { echo "$as_me:$LINENO: WARNING: SQLite4 directory $SQLITE4_DIR does not exist" >&5 +echo "$as_me: WARNING: SQLite4 directory $SQLITE4_DIR does not exist" >&2;} + fi +fi + +SQLITE4_INC=UNKNOWN + +if test -n "$SQLITE4_DIR" ; then + echo "$as_me:$LINENO: checking for SQLite4 header and library" >&5 +echo $ECHO_N "checking for SQLite4 header and library... $ECHO_C" >&6 + for i in $SQLITE4_DIR ; do + if test -r "$i/sqlite4.h" ; then + if test -r "$i/sqlite4.c" ; then + SQLITE4_INC="$i" + SQLITE4_A10N_C="$i/sqlite4.c" + SQLITE4_A10N_O="sqlite4.lo" + break + fi + fi + done +fi +if test "$SQLITE4_INC" = "UNKNOWN" ; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + { echo "$as_me:$LINENO: WARNING: SQLite4 header file and source not found" >&5 +echo "$as_me: WARNING: SQLite4 header file and source not found" >&2;} +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + LIB_TARGETS="$LIB_TARGETS libsqlite4odbc.la" + INST_TARGETS="$INST_TARGETS install-4" + UNINST_TARGETS="$UNINST_TARGETS uninstall-4" + DRVINST_TARGETS="$DRVINST_TARGETS drvinst-4" + DRVUNINST_TARGETS="$DRVUNINST_TARGETS drvuninst-4" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_ENABLE_COLUMN_METADATA=1" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_SOUNDEX=1" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_THREADSAFE=1" +fi + + + + +######### +# libxml2 support for XPath virtual table module +XML2_FLAGS="" +XML2_LIBS="" +EXT_XPATH="" +if test "$SQLITE3_LOADEXTENSION" = "1" ; then + echo "$as_me:$LINENO: checking for libxml2 header and library" >&5 +echo $ECHO_N "checking for libxml2 header and library... $ECHO_C" >&6 + XML2_CONFIG="`which xml2-config 2>/dev/null`" + if test -n "$XML2_CONFIG" ; then + XML2_FLAGS="`xml2-config --cflags`" + XML2_LIBS="`xml2-config --libs`" + EXT_XPATH=libsqlite3_mod_xpath.la + LIB_TARGETS="$LIB_TARGETS $EXT_XPATH" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + fi +fi + + + + ######### # Any SQLite to be done ? @@ -20901,7 +21347,7 @@ fi ########## -# Experimental: dlopen for resolving SQLite3 symbols +# Experimental: dlopen for resolving SQLite3/SQLite4 symbols # # Check whether --with-dls or --without-dls was given. @@ -20915,6 +21361,8 @@ if test "$DLOPENFORGPPS" = "1" ; then if test "$WITH_DLS" = "1" ; then DL_OPTS="-DSQLITE_DYNLOAD=1" SQLITE3_LIB="" + SQLITE3_A10N_O="" + SQLITE4_A10N_O="" DL_INITFINI="-Wl,-init,dls_init -Wl,-fini,dls_fini" fi fi @@ -20922,6 +21370,8 @@ fi + + ######### # Generate the output files. # @@ -21613,7 +22063,6 @@ s,@SQLITE_MPRINTF@,$SQLITE_MPRINTF,;t t s,@SQLITE_TRACE@,$SQLITE_TRACE,;t t s,@SQLITE3_INC@,$SQLITE3_INC,;t t s,@SQLITE3_A10N_C@,$SQLITE3_A10N_C,;t t -s,@SQLITE3_A10N_O@,$SQLITE3_A10N_O,;t t s,@SQLITE3_A10N_FLAGS@,$SQLITE3_A10N_FLAGS,;t t s,@SQLITE3_COLUMNTABLENAME@,$SQLITE3_COLUMNTABLENAME,;t t s,@SQLITE3_OVERLOADFUNCTION@,$SQLITE3_OVERLOADFUNCTION,;t t @@ -21625,8 +22074,17 @@ s,@SQLITE3_TABLECOLUMNMETADATA@,$SQLITE3_TABLECOLUMNMETADATA,;t t s,@SQLITE3_VFS@,$SQLITE3_VFS,;t t s,@SQLITE3_PROFILE@,$SQLITE3_PROFILE,;t t s,@SQLITE3_STRNICMP@,$SQLITE3_STRNICMP,;t t +s,@SQLITE3_CLOSE_V2@,$SQLITE3_CLOSE_V2,;t t s,@EXT_BLOBTOXY@,$EXT_BLOBTOXY,;t t s,@EXT_IMPEXP@,$EXT_IMPEXP,;t t +s,@EXT_CSVTABLE@,$EXT_CSVTABLE,;t t +s,@EXT_ZIPFILE@,$EXT_ZIPFILE,;t t +s,@SQLITE4_INC@,$SQLITE4_INC,;t t +s,@SQLITE4_A10N_C@,$SQLITE4_A10N_C,;t t +s,@SQLITE4_A10N_FLAGS@,$SQLITE4_A10N_FLAGS,;t t +s,@XML2_FLAGS@,$XML2_FLAGS,;t t +s,@XML2_LIBS@,$XML2_LIBS,;t t +s,@EXT_XPATH@,$EXT_XPATH,;t t s,@LIB_TARGETS@,$LIB_TARGETS,;t t s,@INST_TARGETS@,$INST_TARGETS,;t t s,@UNINST_TARGETS@,$UNINST_TARGETS,;t t @@ -21639,6 +22097,8 @@ s,@DRVDIR@,$DRVDIR,;t t s,@DL_OPTS@,$DL_OPTS,;t t s,@DL_INITFINI@,$DL_INITFINI,;t t s,@SQLITE3_LIB@,$SQLITE3_LIB,;t t +s,@SQLITE3_A10N_O@,$SQLITE3_A10N_O,;t t +s,@SQLITE4_A10N_O@,$SQLITE4_A10N_O,;t t s,@SPEC_CHANGELOG_TS@,$SPEC_CHANGELOG_TS,;t t s,@DEB_CHANGELOG_TS@,$DEB_CHANGELOG_TS,;t t s,@LIBOBJS@,$LIBOBJS,;t t diff --git a/lang/sql/odbc/configure.in b/lang/sql/odbc/configure.in index 062c8d4c..774eba7f 100755 --- a/lang/sql/odbc/configure.in +++ b/lang/sql/odbc/configure.in @@ -58,7 +58,7 @@ for i in $SQLITE_DIR ; do done if test "$SQLITE_INC" = "UNKNOWN" -o "$SQLITE_LIB" = "UNKNOWN" ; then AC_MSG_RESULT(no) - AC_MSG_WARN(SQLite header files and/or library not found) + AC_MSG_WARN([SQLite header files and/or library not found]) else AC_MSG_RESULT(yes) fi @@ -90,7 +90,7 @@ AC_SUBST(SQLITE_ATOF) AC_SUBST(SQLITE_MPRINTF) AC_SUBST(SQLITE_TRACE) if test "$SQLITE_COMPILE" = "0" ; then - AC_MSG_WARN(SQLite library too old, need 2.8.0 or later) + AC_MSG_WARN([SQLite library too old, need 2.8.0 or later]) else LIB_TARGETS="$LIB_TARGETS libsqliteodbc.la" INST_TARGETS="$INST_TARGETS install-2" @@ -106,7 +106,7 @@ AC_ARG_WITH(sqlite3,[ --with-sqlite3=DIR use SQLite3 hdr/lib from DIR], SQLITE3_DIR=$withval) if test -n "$SQLITE3_DIR" ; then if test ! -d "$SQLITE3_DIR" ; then - AC_MSG_WARN(SQLite3 directory $SQLITE3_DIR does not exist) + AC_MSG_WARN([SQLite3 directory $SQLITE3_DIR does not exist]) fi fi @@ -155,7 +155,7 @@ for i in $SQLITE3_DIR ; do done if test "$SQLITE3_INC" = "UNKNOWN" -o "$SQLITE3_LIB" = "UNKNOWN" ; then AC_MSG_RESULT(no) - AC_MSG_WARN(SQLite3 header files and/or library not found) + AC_MSG_WARN([SQLite3 header files and/or library not found]) else AC_MSG_RESULT(yes) LIB_TARGETS="$LIB_TARGETS libsqlite3odbc.la" @@ -172,7 +172,6 @@ else fi AC_SUBST(SQLITE3_INC) AC_SUBST(SQLITE3_A10N_C) -AC_SUBST(SQLITE3_A10N_O) AC_SUBST(SQLITE3_A10N_FLAGS) # test for availability of some sqlite3_*() funcs @@ -182,15 +181,17 @@ if test -n "$SQLITE3_LIBDIR" ; then fi if test -n "$SQLITE3_A10N_C" ; then - SQLITE3_LOADEXTENSTION=1 + SQLITE3_LOADEXTENSION=1 SQLITE3_COLUMNTABLENAME=1 SQLITE3_OVERLOADFUNCTION=1 SQLITE3_PREPARE_V2=1 SQLITE3_CLEARBINDINGS=1 SQLITE3_CREATEMODULE_V2=1 - SQLITE3_TABLECOLUMNMETADATA=1 SQLITE3_VFS=1 + SQLITE3_TABLECOLUMNMETADATA=1 + SQLITE3_PROFILE=1 SQLITE3_STRNICMP=1 + SQLITE3_CLOSE_V2=1 else saved_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -I$SQLITE3_INC" @@ -237,17 +238,39 @@ else AC_CHECK_LIB(sqlite3,sqlite3_strnicmp, SQLITE3_STRNICMP=1, SQLITE3_STRNICMP=0) + AC_CHECK_LIB(sqlite3,sqlite3_close_v2, + SQLITE3_CLOSE_V2=1, + SQLITE3_CLOSE_V2=0) LIBS=$saved_LIBS CFLAGS=$saved_CFLAGS fi +######### +# Find zlib for zipfile module +# +LIBZ_OK=no +if test "$SQLITE3_LOADEXTENSION" = "1" ; then + AC_SEARCH_LIBS(zlibVersion,z,[ AC_CHECK_HEADERS(zlib.h,[LIBZ_OK=yes]) ]) +fi + +######### +# Add extensions to build +# if test "$SQLITE3_LOADEXTENSION" = "1" ; then EXT_BLOBTOXY=libsqlite3_mod_blobtoxy.la EXT_IMPEXP=libsqlite3_mod_impexp.la - LIB_TARGETS="$LIB_TARGETS $EXT_BLOBTOXY $EXT_IMPEXP" + EXT_CSVTABLE=libsqlite3_mod_csvtable.la + EXT_ZIPFILE="" + LIB_TARGETS="$LIB_TARGETS $EXT_BLOBTOXY $EXT_IMPEXP $EXT_CSVTABLE" + if test "$LIBZ_OK" = "yes" ; then + EXT_ZIPFILE=libsqlite3_mod_zipfile.la + LIB_TARGETS="$LIB_TARGETS $EXT_ZIPFILE" + fi else EXT_BLOBTOXY="" EXT_IMPEXP="" + EXT_CSVTABLE="" + EXT_ZIPFILE="" fi AC_SUBST(SQLITE3_COLUMNTABLENAME) @@ -260,14 +283,83 @@ AC_SUBST(SQLITE3_TABLECOLUMNMETADATA) AC_SUBST(SQLITE3_VFS) AC_SUBST(SQLITE3_PROFILE) AC_SUBST(SQLITE3_STRNICMP) +AC_SUBST(SQLITE3_CLOSE_V2) AC_SUBST(EXT_BLOBTOXY) AC_SUBST(EXT_IMPEXP) +AC_SUBST(EXT_CSVTABLE) +AC_SUBST(EXT_ZIPFILE) + +########## +# Find SQLite4 header file and library +# +AC_ARG_WITH(sqlite4,[ --with-sqlite4=DIR use SQLite4 hdr/src from DIR], + SQLITE4_DIR=$withval) +if test -n "$SQLITE4_DIR" ; then + if test ! -d "$SQLITE4_DIR" ; then + AC_MSG_WARN([SQLite4 directory $SQLITE4_DIR does not exist]) + fi +fi + +SQLITE4_INC=UNKNOWN + +if test -n "$SQLITE4_DIR" ; then + AC_MSG_CHECKING([for SQLite4 header and library]) + for i in $SQLITE4_DIR ; do + if test -r "$i/sqlite4.h" ; then + if test -r "$i/sqlite4.c" ; then + SQLITE4_INC="$i" + SQLITE4_A10N_C="$i/sqlite4.c" + SQLITE4_A10N_O="sqlite4.lo" + break + fi + fi + done +fi +if test "$SQLITE4_INC" = "UNKNOWN" ; then + AC_MSG_RESULT(no) + AC_MSG_WARN([SQLite4 header file and source not found]) +else + AC_MSG_RESULT(yes) + LIB_TARGETS="$LIB_TARGETS libsqlite4odbc.la" + INST_TARGETS="$INST_TARGETS install-4" + UNINST_TARGETS="$UNINST_TARGETS uninstall-4" + DRVINST_TARGETS="$DRVINST_TARGETS drvinst-4" + DRVUNINST_TARGETS="$DRVUNINST_TARGETS drvuninst-4" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_ENABLE_COLUMN_METADATA=1" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_SOUNDEX=1" + SQLITE4_A10N_FLAGS="$SQLITE4_A10N_FLAGS -DSQLITE4_THREADSAFE=1" +fi +AC_SUBST(SQLITE4_INC) +AC_SUBST(SQLITE4_A10N_C) +AC_SUBST(SQLITE4_A10N_FLAGS) + +######### +# libxml2 support for XPath virtual table module +XML2_FLAGS="" +XML2_LIBS="" +EXT_XPATH="" +if test "$SQLITE3_LOADEXTENSION" = "1" ; then + AC_MSG_CHECKING([for libxml2 header and library]) + XML2_CONFIG="`which xml2-config 2>/dev/null`" + if test -n "$XML2_CONFIG" ; then + XML2_FLAGS="`xml2-config --cflags`" + XML2_LIBS="`xml2-config --libs`" + EXT_XPATH=libsqlite3_mod_xpath.la + LIB_TARGETS="$LIB_TARGETS $EXT_XPATH" + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +fi +AC_SUBST(XML2_FLAGS) +AC_SUBST(XML2_LIBS) +AC_SUBST(EXT_XPATH) ######### # Any SQLite to be done ? if test "X$LIB_TARGETS" = "X" ; then - AC_MSG_ERROR(No usable SQLite header/library on this system) + AC_MSG_ERROR([No usable SQLite header/library on this system]) fi AC_SUBST(LIB_TARGETS) AC_SUBST(INST_TARGETS) @@ -282,7 +374,7 @@ AC_ARG_WITH(odbc, [ --with-odbc=DIR use ODBC header/libs from DIR], ODBC_DIR=$withval) if test -n "$ODBC_DIR" ; then if test ! -d "$ODBC_DIR" ; then - AC_MSG_ERROR(ODBC directory $ODBC_DIR does not exist) + AC_MSG_ERROR([ODBC directory $ODBC_DIR does not exist]) fi fi @@ -349,7 +441,7 @@ for i in $ODBC_DIR ; do done if test "$ODBC_FLAGS" = "UNKNOWN" -o "$ODBC_LIB" = "UNKNOWN" ; then AC_MSG_RESULT(no) - AC_MSG_ERROR(ODBC header files and/or libraries not found) + AC_MSG_ERROR([ODBC header files and/or libraries not found]) else AC_MSG_RESULT(yes) fi @@ -607,20 +699,24 @@ fi AC_SUBST(DRVDIR) ########## -# Experimental: dlopen for resolving SQLite3 symbols +# Experimental: dlopen for resolving SQLite3/SQLite4 symbols # -AC_ARG_WITH(dls,[ --with-dls dlopen SQLite3 lib], +AC_ARG_WITH(dls,[ --with-dls dlopen SQLite3/SQLite4 lib], WITH_DLS=1,WITH_DLS=0) if test "$DLOPENFORGPPS" = "1" ; then if test "$WITH_DLS" = "1" ; then DL_OPTS="-DSQLITE_DYNLOAD=1" SQLITE3_LIB="" + SQLITE3_A10N_O="" + SQLITE4_A10N_O="" DL_INITFINI="-Wl,-init,dls_init -Wl,-fini,dls_fini" fi fi AC_SUBST(DL_OPTS) AC_SUBST(DL_INITFINI) AC_SUBST(SQLITE3_LIB) +AC_SUBST(SQLITE3_A10N_O) +AC_SUBST(SQLITE4_A10N_O) ######### # Generate the output files. diff --git a/lang/sql/odbc/csvtable.c b/lang/sql/odbc/csvtable.c new file mode 100644 index 00000000..112b19a5 --- /dev/null +++ b/lang/sql/odbc/csvtable.c @@ -0,0 +1,1616 @@ +/** + * @file csvtable.c + * SQLite extension module for mapping a CSV file as + * a read-only SQLite virtual table plus extension + * function to import a CSV file as a real table. + * + * 2012 July 27 + * + * The author disclaims copyright to this source code. In place of + * a legal notice, here is a blessing: + * + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + * + ******************************************************************** + */ + +#ifdef STANDALONE +#include <sqlite3.h> +#else +#include <sqlite3ext.h> +static SQLITE_EXTENSION_INIT1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#ifdef _WIN32 +#include <windows.h> +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#endif + +/** + * @typedef csv_file + * @struct csv_file + * Structure to implement CSV file handle. + */ + +typedef struct csv_file { + FILE *f; /**< CSV file */ + char *sep; /**< column separator characters */ + char *quot; /**< text quoting characters */ + int isdos; /**< true, when DOS format detected */ + int maxl; /**< max. capacity of line buffer */ + char *line; /**< line buffer */ + long pos0; /**< file position for rewind */ + int maxc; /**< max. capacity of column buffer */ + int ncols; /**< number of columns */ + char **cols; /**< column buffer */ +} csv_file; + +/** + * @typedef csv_guess_fmt + * @struct csv_guess_fmt + * Info to guess CSV layout. + */ + +typedef struct csv_guess_fmt { + int nlines; + int hist[256]; +} csv_guess_fmt; + +/** + * @typedef csv_vtab + * @struct csv_vtab + * Structure to describe a CSV virtual table. + */ + +typedef struct csv_vtab { + sqlite3_vtab vtab; /**< SQLite virtual table */ + csv_file *csv; /**< CSV file handle */ + int convert; /**< convert flags */ + char coltypes[1]; /**< column types */ +} csv_vtab; + +/** + * @typedef csv_cursor + * @struct csv_cursor + * Structure to describe CSV virtual table cursor. + */ + +typedef struct { + sqlite3_vtab_cursor cursor; /**< SQLite virtual table cursor */ + long pos; /**< CSV file position */ +} csv_cursor; + +/** + * Free dynamically allocated string buffer + * @param in input string pointer + */ + +static void +append_free(char **in) +{ + long *p = (long *) *in; + + if (p) { + p -= 2; + sqlite3_free(p); + *in = 0; + } +} + +/** + * Append a string to dynamically allocated string buffer + * with optional quoting + * @param in input string pointer + * @param append string to append + * @param quote quote character or NUL + * @result new string to be free'd with append_free() + */ + +static char * +append(char **in, char const *append, char quote) +{ + long *p = (long *) *in; + long len, maxlen, actlen; + int i; + char *pp; + int nappend = append ? strlen(append) : 0; + + if (p) { + p -= 2; + maxlen = p[0]; + actlen = p[1]; + } else { + maxlen = actlen = 0; + } + len = nappend + actlen; + if (quote) { + len += 2; + for (i = 0; i < nappend; i++) { + if (append[i] == quote) { + len++; + } + } + } else if (!nappend) { + return *in; + } + if (len >= maxlen - 1) { + long *q; + + maxlen = (len + 0x03ff) & (~0x3ff); + q = (long *) sqlite3_realloc(p, maxlen + 1 + 2 * sizeof (long)); + if (!q) { + return 0; + } + if (!p) { + q[1] = 0; + } + p = q; + p[0] = maxlen; + *in = (char *) (p + 2); + } + pp = *in + actlen; + if (quote) { + *pp++ = quote; + for (i = 0; i < nappend; i++) { + *pp++ = append[i]; + if (append[i] == quote) { + *pp++ = quote; + } + } + *pp++ = quote; + *pp = '\0'; + } else { + if (nappend) { + memcpy(pp, append, nappend); + pp += nappend; + *pp = '\0'; + } + } + p[1] = pp - *in; + return *in; +} + +/** + * Strip off quotes given string. + * @param in string to be processed + * @result new string to be free'd with sqlite3_free() + */ + +static char * +unquote(char const *in) +{ + char c, *ret; + int i; + + ret = sqlite3_malloc(strlen(in) + 1); + if (ret) { + c = in[0]; + if ((c == '"') || (c == '\'')) { + i = strlen(in + 1); + if ((i > 0) && (in[i] == c)) { + strcpy(ret, in + 1); + ret[i - 1] = '\0'; + return ret; + } + } + strcpy(ret, in); + } + return ret; +} + +/** + * Map string to SQLite data type. + * @param type string to be mapped + * @result SQLITE_TEXT et.al. + */ + +static int +maptype(char const *type) +{ + int typelen = type ? strlen(type) : 0; + + if ((typelen >= 3) && + (strncasecmp(type, "integer", 7) == 0)) { + return SQLITE_INTEGER; + } + if ((typelen >= 6) && + (strncasecmp(type, "double", 6) == 0)) { + return SQLITE_FLOAT; + } + if ((typelen >= 5) && + (strncasecmp(type, "float", 5) == 0)) { + return SQLITE_FLOAT; + } + if ((typelen >= 4) && + (strncasecmp(type, "real", 4) == 0)) { + return SQLITE_FLOAT; + } + return SQLITE_TEXT; +} + +/** + * Convert and collapse white space in column names to underscore + * @param names string vector of column names + * @param ncols number of columns + */ + +static void +conv_names(char **names, int ncols) +{ + int i; + char *p, *q; + static const char ws[] = "\n\t\r\b\v "; + + if (!names || ncols <= 0) { + return; + } + for (i = 0; i < ncols; i++) { + p = names[i]; + + while (*p) { + if (strchr(ws, *p)) { + *p++ = '_'; + q = p; + while (*q && strchr(ws, *q)) { + ++q; + } + if (*q && q > p) { + strcpy(p, q); + } + continue; + } + ++p; + } + } +} + +/** + * Make result data or parameter binding accoring to type + * @param ctx SQLite function context or NULL + * @param stmt SQLite statement or NULL + * @param idx parameter number, 1-based + * @param data string data + * @param len string length + * @param type SQLite type + */ + +static void +result_or_bind(sqlite3_context *ctx, sqlite3_stmt *stmt, int idx, + char *data, int len, int type) +{ + char *endp; + + if (!data) { + if (ctx) { + sqlite3_result_null(ctx); + } else { + sqlite3_bind_null(stmt, idx); + } + return; + } + if (type == SQLITE_INTEGER) { + sqlite_int64 val; +#if defined(_WIN32) || defined(_WIN64) + char endc; + + if (sscanf(data, "%I64d%c", &val, &endc) == 1) { + if (ctx) { + sqlite3_result_int64(ctx, val); + } else { + sqlite3_bind_int64(stmt, idx, val); + } + return; + } +#else + endp = 0; +#ifdef __osf__ + val = strtol(data, &endp, 0); +#else + val = strtoll(data, &endp, 0); +#endif + if (endp && (endp != data) && !*endp) { + if (ctx) { + sqlite3_result_int64(ctx, val); + } else { + sqlite3_bind_int64(stmt, idx, val); + } + return; + } +#endif + } else if (type == SQLITE_FLOAT) { + double val; + + endp = 0; + val = strtod(data, &endp); + if (endp && (endp != data) && !*endp) { + if (ctx) { + sqlite3_result_double(ctx, val); + } else { + sqlite3_bind_double(stmt, idx, val); + } + return; + } + } + if (ctx) { + sqlite3_result_text(ctx, data, len, SQLITE_TRANSIENT); + } else { + sqlite3_bind_text(stmt, idx, data, len, SQLITE_TRANSIENT); + } +} + +/** + * Process one column of the current row + * @param ctx SQLite function context or NULL + * @param stmt SQLite statement or NULL + * @param idx parameter index, 1-based + * @param data string data + * @param type SQLite type + * @param conv conversion flags + */ + +static int +process_col(sqlite3_context *ctx, sqlite3_stmt *stmt, int idx, + char *data, int type, int conv) +{ + char c, *p; + const char flchars[] = "Ee+-.,0123456789"; + + if (!data) { + goto putdata; + } + + /* + * Floating point number test, + * converts single comma to dot. + */ + c = data[0]; + if ((c != '\0') && strchr(flchars + 2, c)) { + p = data + 1; + while (*p && strchr(flchars, *p)) { + ++p; + } + if (*p == '\0') { + char *first = 0; + int n = 0; + + p = data; + while (p) { + p = strchr(p, ','); + if (!p) { + break; + } + if (++n == 1) { + first = p; + } + ++p; + } + if (first) { + *first = '.'; + goto putdata; + } + } + } + if (conv) { + char *utf = sqlite3_malloc(strlen(data) * 2 + 2); + + if (utf) { + p = utf; + while ((c = *data) != '\0') { + if (((conv & 10) == 10) && (c == '\\')) { + if (data[1] == 'q') { + *p++ = '\''; + data += 2; + continue; + } + } + if ((conv & 2) && (c == '\\')) { + char c2 = data[1]; + + switch (c2) { + case '\0': + goto convdone; + case 'n': + *p = '\n'; + break; + case 't': + *p = '\t'; + break; + case 'r': + *p = '\r'; + break; + case 'f': + *p = '\f'; + break; + case 'v': + *p = '\v'; + break; + case 'b': + *p = '\b'; + break; + case 'a': + *p = '\a'; + break; + case '?': + *p = '\?'; + break; + case '\'': + *p = '\''; + break; + case '"': + *p = '\"'; + break; + case '\\': + *p = '\\'; + break; + default: + *p++ = c; + *p = c2; + break; + } + p++; + data += 2; + continue; + } + if ((conv & 1) && (c & 0x80)) { + *p++ = 0xc0 | ((c >> 6) & 0x1f); + *p++ = 0x80 | (c & 0x3f); + } else { + *p++ = c; + } + data++; + } +convdone: + *p = '\0'; + result_or_bind(ctx, stmt, idx, utf, p - utf, type); + sqlite3_free(utf); + return SQLITE_OK; + } else { + if (ctx) { + sqlite3_result_error(ctx, "out of memory", -1); + } + return SQLITE_NOMEM; + } + } +putdata: + result_or_bind(ctx, stmt, idx, data, -1, type); + return SQLITE_OK; +} + +/** + * Open CSV file for reading and return handle to it. + * @param filename name of CSV file + * @param sep column separator characters or NULL + * @param quot string quote characters or NULL + * @result CSV file handle + */ + +static csv_file * +csv_open(const char *filename, const char *sep, const char *quot) +{ + FILE *f; + csv_file *csv; + +#ifdef _WIN32 + f = fopen(filename, "rb"); +#else + f = fopen(filename, "r"); +#endif + if (!f) { + return 0; + } + csv = sqlite3_malloc(sizeof (csv_file)); + if (!csv) { +error0: + fclose(f); + return 0; + } + csv->f = f; + if (sep && sep[0]) { + csv->sep = sqlite3_malloc(strlen(sep) + 1); + if (!csv->sep) { +error1: + sqlite3_free(csv); + goto error0; + } + strcpy(csv->sep, sep); + } else { + csv->sep = 0; + } + if (quot && quot[0]) { + csv->quot = sqlite3_malloc(strlen(quot) + 1); + if (!csv->quot) { + if (csv->sep) { + sqlite3_free(csv->sep); + } + goto error1; + } + strcpy(csv->quot, quot); + } else { + csv->quot = 0; + } + csv->isdos = 0; + csv->maxl = 0; + csv->line = 0; + csv->pos0 = 0; + csv->maxc = 0; + csv->ncols = 0; + csv->cols = 0; + return csv; +} + +/** + * Close CSV file handle. + * @param csv CSV file handle + */ + +static void +csv_close(csv_file *csv) +{ + if (csv) { + if (csv->sep) { + sqlite3_free(csv->sep); + } + if (csv->quot) { + sqlite3_free(csv->quot); + } + if (csv->line) { + sqlite3_free(csv->line); + } + if (csv->cols) { + sqlite3_free(csv->cols); + } + if (csv->f) { + fclose(csv->f); + } + sqlite3_free(csv); + } +} + +/** + * Test EOF on CSV file handle. + * @param csv CSV file handle + * @result true when file position is at EOF + */ + +static int +csv_eof(csv_file *csv) +{ + if (csv && csv->f) { + return feof(csv->f); + } + return 1; +} + +/** + * Position CSV file handle. + * @param csv CSV file handle + * @param pos position to seek + * @result 0 on success, EOF on error + */ + +static long +csv_seek(csv_file *csv, long pos) +{ + if (csv && csv->f) { + return fseek(csv->f, pos, SEEK_SET); + } + return EOF; +} + +/** + * Rewind CSV file handle. + * @param csv CSV file handle + */ + +static void +csv_rewind(csv_file *csv) +{ + if (csv && csv->f) { + csv_seek(csv, csv->pos0); + } +} + +/** + * Return current position of CSV file handle. + * @param csv CSV file handle + * @result current file position + */ + +static long +csv_tell(csv_file *csv) +{ + if (csv && csv->f) { + return ftell(csv->f); + } + return EOF; +} + +/** + * Read and process one line of CSV file handle. + * @param csv CSV file handle + * @param guess NULL or buffer for guessing file format + * @result number of columns on success, EOF on error + */ + +static int +csv_getline(csv_file *csv, csv_guess_fmt *guess) +{ + int i, index = 0, inq = 0, c, col; + char *p, *sep; + + if (!csv || !csv->f) { + return EOF; + } + while (1) { + c = fgetc(csv->f); + if (c == EOF) { + if (index > 0) { + break; + } + return EOF; + } + if (c == '\0') { + continue; + } + if (c == '\r') { + int c2 = fgetc(csv->f); + c = '\n'; + + if (c2 == '\n') { + csv->isdos = 1; + } else if (c2 != EOF) { + ungetc(c2, csv->f); + } + } + /* check for DOS EOF (Ctrl-Z) */ + if (csv->isdos && (c == '\032')) { + int c2 = fgetc(csv->f); + + if (c2 == EOF) { + if (index > 0) { + break; + } + return EOF; + } + ungetc(c2, csv->f); + } + if (index >= csv->maxl - 1) { + int n = csv->maxl * 2; + char *line; + + if (n <= 0) { + n = 4096; + } + line = sqlite3_malloc(n); + if (!line) { + return EOF; + } + if (csv->line) { + memcpy(line, csv->line, index); + sqlite3_free(csv->line); + } + csv->maxl = n; + csv->line = line; + } + csv->line[index++] = c; + if (csv->quot && (p = strchr(csv->quot, c))) { + if (inq) { + if (*p == inq) { + inq = 0; + } + } else { + inq = *p; + } + } + if (!inq && (c == '\n')) { + break; + } + } + if (guess) { + for (i = 0; i < index; i++) { + guess->hist[csv->line[i] & 0xFF] += 1; + } + guess->nlines += 1; + csv->ncols = 0; + return 0; + } + + for (i = index - 1; i >= 0; i--) { + if (csv->line[i] != '\n') { + break; + } + } + index = i + 1; + csv->line[index] = '\0'; + i = inq = col = 0; + sep = csv->sep ? csv->sep : ";"; + if (!csv->cols) { + int n = 128; + + csv->cols = sqlite3_malloc(sizeof (char *) * n); + if (!csv->cols) { + return EOF; + } + csv->maxc = n; + } + csv->cols[col++] = csv->line; + while (i < index) { + if (csv->quot && (p = strchr(csv->quot, csv->line[i]))) { + if (inq) { + if (*p == inq) { + inq = 0; + } + } else { + inq = *p; + } + } + if (!inq && (p = strchr(sep, csv->line[i]))) { + p = csv->line + i; + *p = '\0'; + if (col >= csv->maxc) { + int n = csv->maxc * 2; + char **cols; + + cols = sqlite3_realloc(csv->cols, sizeof (char *) * n); + if (!cols) { + return EOF; + } + csv->cols = cols; + csv->maxc = n; + } + csv->cols[col++] = p + 1; + } + ++i; + } + csv->ncols = col; + + /* strip off quotes */ + if (csv->quot) { + for (i = 0; i < col; i++) { + if (*csv->cols[i]) { + p = strchr(csv->quot, *csv->cols[i]); + if (p) { + char *src, *dst; + + c = *p; + csv->cols[i] += 1; + sep = csv->cols[i] + strlen(csv->cols[i]) - 1; + if ((sep >= csv->cols[i]) && (*sep == c)) { + *sep = '\0'; + } + /* collapse quote escape sequences */ + src = csv->cols[i]; + dst = 0; + while (*src) { + if ((*src == c) && (src[1] == c)) { + if (!dst) { + dst = src; + } + src++; + while (*src) { + *dst++ = *src++; + if (*src == c) { + --src; + break; + } + } + } + ++src; + } + if (dst) { + *dst++ = '\0'; + } + } + } + } + } + return col; +} + +/** + * Return number of columns of current row in CSV file. + * @param csv CSV file handle + * @result number of columns of current row + */ + +static int +csv_ncols(csv_file *csv) +{ + if (csv && csv->cols) { + return csv->ncols; + } + return 0; +} + +/** + * Return nth column of current row in CSV file. + * @param csv CSV file handle + * @param n column number + * @result string pointer or NULL + */ + +static char * +csv_coldata(csv_file *csv, int n) +{ + if (csv && csv->cols && (n >= 0) && (n < csv->ncols)) { + return csv->cols[n]; + } + return 0; +} + +/** + * Guess CSV layout of CSV file handle. + * @param csv CSV file handle + * @result 0 on succes, EOF on error + */ + +static int +csv_guess(csv_file *csv) +{ + csv_guess_fmt guess; + int i, n; + char *p, sep[32], quot[4]; + const struct { + int c; + int min; + } sep_test[] = { + { ',', 2 }, + { ';', 2 }, + { '\t', 2 }, + { ' ', 4 }, + { '|', 2 } + }; + + if (!csv) { + return EOF; + } + memset(&guess, 0, sizeof (guess)); + csv->pos0 = 0; + csv_rewind(csv); + for (i = n = 0; i < 10; i++) { + n = csv_getline(csv, &guess); + if (n == EOF) { + break; + } + } + csv_rewind(csv); + if (n && !i) { + return EOF; + } + p = quot; + n = '"'; + if (guess.hist[n] > 1) { + *p++ = n; + } + n = '\''; + if (guess.hist[n] > 1) { + *p++ = n; + } + *p = '\0'; + p = sep; + for (i = 0; i < sizeof (sep_test) / sizeof (sep_test[0]); i++) { + if (guess.hist[sep_test[i].c] > sep_test[i].min * guess.nlines) { + *p++ = sep_test[i].c; + } + } + *p = '\0'; + if (quot[0]) { + p = sqlite3_malloc(strlen(quot) + 1); + if (p) { + strcpy(p, quot); + if (csv->quot) { + sqlite3_free(csv->quot); + } + csv->quot = p; + } else { + return EOF; + } + } + if (sep[0]) { + p = sqlite3_malloc(strlen(sep) + 1); + if (p) { + strcpy(p, sep); + if (csv->sep) { + sqlite3_free(csv->sep); + } + csv->sep = p; + } else { + return EOF; + } + } + return 0; +} + +/** + * Connect to virtual table + * @param db SQLite database pointer + * @param aux user specific pointer (unused) + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + * + * Argument vector contains: + * + * argv[0] - module name<br> + * argv[1] - database name<br> + * argv[2] - table name (virtual table)<br> + * argv[3] - filename (required)<br> + * argv[4] - number, when non-zero use first line as column names, + * when negative use given type names (optional)<br> + * argv[5] - number, when non-zero, translate data (optional, see below)<br> + * argv[6] - column separator characters (optional)<br> + * argv[7] - string quoting characters (optional)<br> + * argv[8] - column/type name for first column (optional)<br> + * ..<br> + * argv[X] - column/type name for last column (optional)<br><br> + * + * Translation flags: + * + * 1 - convert ISO-8859-1 to UTF-8<br> + * 2 - perform backslash substitution<br> + * 4 - convert and collapse white-space in column names to underscore<br> + * 10 - convert \q to single quote, in addition to backslash substitution<br> + */ + +static int +csv_vtab_connect(sqlite3* db, void *aux, int argc, const char * const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + csv_file *csv; + int rc = SQLITE_ERROR, i, ncnames, row1; + char **cnames, *schema = 0, **nargv; + csv_vtab *vtab; + + if (argc < 4) { + *errp = sqlite3_mprintf("input file name missing"); + return SQLITE_ERROR; + } + nargv = sqlite3_malloc(sizeof (char *) * argc); + memset(nargv, 0, sizeof (char *) * argc); + for (i = 3; i < argc; i++) { + nargv[i] = unquote(argv[i]); + } + csv = csv_open(nargv[3], (argc > 6) ? nargv[6] : 0, + (argc > 7) ? nargv[7] : 0); + if (!csv) { + *errp = sqlite3_mprintf("unable to open input file"); +cleanup: + append_free(&schema); + for (i = 3; i < argc; i++) { + if (nargv[i]) { + sqlite3_free(nargv[i]); + } + } + return rc; + } + if (!csv->sep && !csv->quot) { + csv_guess(csv); + } + csv->pos0 = 0; + row1 = 0; + if (argc > 4) { + row1 = strtol(nargv[4], 0, 10); + } + if (row1) { + /* use column names from 1st row */ + csv_getline(csv, 0); + if (csv->ncols < 1) { + csv_close(csv); + *errp = sqlite3_mprintf("unable to get column names"); + goto cleanup; + } + csv->pos0 = csv_tell(csv); + csv_rewind(csv); + ncnames = csv_ncols(csv); + cnames = csv->cols; + } else if (argc > 8) { + ncnames = argc - 8; + cnames = (char **) nargv + 8; + } else { + /* use number of columns from 1st row */ + csv_getline(csv, 0); + if (csv->ncols < 1) { + csv_close(csv); + *errp = sqlite3_mprintf("unable to get column names"); + goto cleanup; + } + csv_rewind(csv); + ncnames = csv_ncols(csv); + cnames = 0; + } + vtab = sqlite3_malloc(sizeof(csv_vtab) + ncnames); + if (!vtab) { + csv_close(csv); + *errp = sqlite3_mprintf("out of memory"); + goto cleanup; + } + memset(vtab, 0, sizeof (*vtab)); + vtab->convert = 0; + if (argc > 5) { + vtab->convert = strtol(nargv[5], 0, 10); + if (row1 && (vtab->convert & 4)) { + conv_names(cnames, ncnames); + } + } + vtab->csv = csv; + append(&schema, "CREATE TABLE x(", 0); + for (i = 0; i < ncnames; i++) { + vtab->coltypes[i] = SQLITE_TEXT; + if (!cnames || !cnames[i]) { + char colname[64]; + + sprintf(colname, "column_%d", i + 1); + append(&schema, colname, '"'); + } else if (row1 > 0) { + append(&schema, cnames[i], '"'); + } else if (row1 < 0) { + append(&schema, cnames[i], '"'); + if (i + 8 < argc) { + char *type = nargv[i + 8]; + + append(&schema, " ", 0); + append(&schema, type, 0); + vtab->coltypes[i] = maptype(type); + } + } else { + char *type = cnames[i]; + + append(&schema, cnames[i], 0); + while (*type && !strchr(" \t", *type)) { + type++; + } + while (*type && strchr(" \t", *type)) { + type++; + } + vtab->coltypes[i] = maptype(type); + } + if (i < ncnames - 1) { + append(&schema, ",", 0); + } + } + append(&schema, ")", 0); + rc = sqlite3_declare_vtab(db, schema); + if (rc != SQLITE_OK) { + csv_close(csv); + sqlite3_free(vtab); + *errp = sqlite3_mprintf("table definition failed, error %d, " + "schema '%s'", rc, schema); + goto cleanup; + } + *vtabp = &vtab->vtab; + *errp = 0; + goto cleanup; +} + +/** + * Create virtual table + * @param db SQLite database pointer + * @param aux user specific pointer (unused) + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + */ + +static int +csv_vtab_create(sqlite3* db, void *aux, int argc, + const char *const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + return csv_vtab_connect(db, aux, argc, argv, vtabp, errp); +} + +/** + * Disconnect virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +csv_vtab_disconnect(sqlite3_vtab *vtab) +{ + csv_vtab *tab = (csv_vtab *) vtab; + + csv_close(tab->csv); + sqlite3_free(tab); + return SQLITE_OK; +} + +/** + * Destroy virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +csv_vtab_destroy(sqlite3_vtab *vtab) +{ + return csv_vtab_disconnect(vtab); +} + +/** + * Determines information for filter function according to constraints. + * @param vtab virtual table + * @param info index/constraint iinformation + * @result SQLite error code + */ + +static int +csv_vtab_bestindex(sqlite3_vtab *vtab, sqlite3_index_info *info) +{ + return SQLITE_OK; +} + +/** + * Open virtual table and return cursor. + * @param vtab virtual table pointer + * @param cursorp pointer receiving cursor pointer + * @result SQLite error code + */ + +static int +csv_vtab_open(sqlite3_vtab *vtab, sqlite3_vtab_cursor **cursorp) +{ + csv_cursor *cur = sqlite3_malloc(sizeof(*cur)); + csv_vtab *tab = (csv_vtab *) vtab; + + if (!cur) { + return SQLITE_ERROR; + } + cur->cursor.pVtab = vtab; + csv_rewind(tab->csv); + cur->pos = csv_tell(tab->csv); + *cursorp = &cur->cursor; + return SQLITE_OK; +} + +/** + * Close virtual table cursor. + * @param cursor cursor pointer + * @result SQLite error code + */ + +static int +csv_vtab_close(sqlite3_vtab_cursor *cursor) +{ + sqlite3_free(cursor); + return SQLITE_OK; +} + +/** + * Retrieve next row from virtual table cursor + * @param cursor virtual table cursor + * @result SQLite error code + */ + +static int +csv_vtab_next(sqlite3_vtab_cursor *cursor) +{ + csv_cursor *cur = (csv_cursor *) cursor; + csv_vtab *tab = (csv_vtab *) cur->cursor.pVtab; + + cur->pos = csv_tell(tab->csv); + csv_getline(tab->csv, 0); + return SQLITE_OK; +} + +/** + * Filter function for virtual table. + * @param cursor virtual table cursor + * @param idxNum unused (always 0) + * @param idxStr unused + * @param argc number arguments (unused, 0) + * @param argv argument (nothing) + * @result SQLite error code + */ + +static int +csv_vtab_filter(sqlite3_vtab_cursor *cursor, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) +{ + csv_cursor *cur = (csv_cursor *) cursor; + csv_vtab *tab = (csv_vtab *) cur->cursor.pVtab; + + csv_rewind(tab->csv); + return csv_vtab_next(cursor); +} + +/** + * Return end of table state of virtual table cursor + * @param cursor virtual table cursor + * @result true/false + */ + +static int +csv_vtab_eof(sqlite3_vtab_cursor *cursor) +{ + csv_cursor *cur = (csv_cursor *) cursor; + csv_vtab *tab = (csv_vtab *) cur->cursor.pVtab; + + return csv_eof(tab->csv); +} + +/** + * Return column data of virtual table. + * @param cursor virtual table cursor + * @param ctx SQLite function context + * @param n column index + * @result SQLite error code + */ + +static int +csv_vtab_column(sqlite3_vtab_cursor *cursor, sqlite3_context *ctx, int n) +{ + csv_cursor *cur = (csv_cursor *) cursor; + csv_vtab *tab = (csv_vtab *) cur->cursor.pVtab; + char *data = csv_coldata(tab->csv, n); + + return process_col(ctx, 0, 0, data, tab->coltypes[n], tab->convert); +} + +/** + * Return current rowid of virtual table cursor + * @param cursor virtual table cursor + * @param rowidp value buffer to receive current rowid + * @result SQLite error code + */ + +static int +csv_vtab_rowid(sqlite3_vtab_cursor *cursor, sqlite_int64 *rowidp) +{ + csv_cursor *cur = (csv_cursor *) cursor; + + *rowidp = cur->pos; + return SQLITE_OK; +} + +#if (SQLITE_VERSION_NUMBER > 3004000) + +/** + * Rename virtual table. + * @param newname new name for table + * @result SQLite error code + */ + +static int +csv_vtab_rename(sqlite3_vtab *vtab, const char *newname) +{ + return SQLITE_OK; +} + +#endif + +/** + * SQLite module descriptor. + */ + +static const sqlite3_module csv_vtab_mod = { + 1, /* iVersion */ + csv_vtab_create, /* xCreate */ + csv_vtab_connect, /* xConnect */ + csv_vtab_bestindex, /* xBestIndex */ + csv_vtab_disconnect, /* xDisconnect */ + csv_vtab_destroy, /* xDestroy */ + csv_vtab_open, /* xOpen */ + csv_vtab_close, /* xClose */ + csv_vtab_filter, /* xFilter */ + csv_vtab_next, /* xNext */ + csv_vtab_eof, /* xEof */ + csv_vtab_column, /* xColumn */ + csv_vtab_rowid, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindFunction */ +#if (SQLITE_VERSION_NUMBER > 3004000) + csv_vtab_rename, /* xRename */ +#endif +}; + +/** + * Import CSV file as table into database + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Argument vector contains: + * + * argv[0] - name of table to create (required)<br> + * argv[1] - filename (required)<br> + * argv[2] - number, when non-zero use first line as column names, + * when negative use given type names (optional)<br> + * argv[3] - number, when non-zero, translate data (optional, see below)<br> + * argv[4] - column separator characters (optional)<br> + * argv[5] - string quoting characters (optional)<br> + * argv[6] - column/type name for first column (optional)<br> + * ..<br> + * argv[X] - column/type name for last column (optional)<br><br> + * + * Translation flags: + * + * 1 - convert ISO-8859-1 to UTF-8<br> + * 2 - perform backslash substitution<br> + * 4 - convert and collapse white-space in column names to underscore<br> + * 10 - convert \q to single quote, in addition to backslash substitution<br> + */ + +static void +csv_import_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + csv_file *csv; + int rc, i, ncnames, row1, convert = 0, useargs = 0; + char *tname, *fname, *sql = 0, **cnames, *coltypes = 0; + sqlite3 *db = (sqlite3 *) sqlite3_user_data(ctx); + sqlite3_stmt *stmt = 0; + + if (argc < 2) { + sqlite3_result_error(ctx, "need at least 2 arguments", -1); + return; + } + tname = (char *) sqlite3_value_text(argv[0]); + if (!tname) { + sqlite3_result_error(ctx, "table name is NULL", -1); + return; + } + fname = (char *) sqlite3_value_text(argv[1]); + if (!fname) { + sqlite3_result_error(ctx, "file name is NULL", -1); + return; + } + csv = csv_open(fname, + (argc > 4) ? (char *) sqlite3_value_text(argv[4]) : 0, + (argc > 5) ? (char *) sqlite3_value_text(argv[5]) : 0); + if (!csv) { + sqlite3_result_error(ctx, "unable to open input file", -1); +cleanup: + if (stmt) { + sqlite3_finalize(stmt); + } + append_free(&sql); + if (coltypes) { + sqlite3_free(coltypes); + } + if (csv) { + csv_close(csv); + } + return; + } + if (!csv->sep && !csv->quot) { + csv_guess(csv); + } + csv->pos0 = 0; + row1 = 0; + if (argc > 2) { + row1 = sqlite3_value_int(argv[2]); + } + if (row1) { + /* use column names from 1st row */ + csv_getline(csv, 0); + if (csv->ncols < 1) { + sqlite3_result_error(ctx, "unable to get column names", -1); + goto cleanup; + } + csv->pos0 = csv_tell(csv); + csv_rewind(csv); + ncnames = csv_ncols(csv); + cnames = csv->cols; + } else if (argc > 6) { + ncnames = argc - 6; + cnames = 0; + useargs = 1; + } else { + /* use number of columns from 1st row */ + csv_getline(csv, 0); + if (csv->ncols < 1) { + sqlite3_result_error(ctx, "unable to get column names", -1); + goto cleanup; + } + csv_rewind(csv); + ncnames = csv_ncols(csv); + cnames = 0; + } + convert = 0; + if (argc > 3) { + convert = sqlite3_value_int(argv[3]); + if (row1 && (convert & 4)) { + conv_names(cnames, ncnames); + } + } + /* test if table exists */ + append(&sql, "PRAGMA table_info(", 0); + append(&sql, tname, '"'); + append(&sql, ")", 0); + if (!sql) { +oom: + sqlite3_result_error(ctx, "out of memory", -1); + goto cleanup; + } + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + append_free(&sql); + if (rc != SQLITE_OK) { +prepfail: + sqlite3_result_error(ctx, "prepare failed", -1); + goto cleanup; + } + /* find number of colums */ + i = 0; + rc = sqlite3_step(stmt); + while (rc == SQLITE_ROW) { + i++; + rc = sqlite3_step(stmt); + } + if (rc != SQLITE_DONE) { +selfail: + sqlite3_result_error(ctx, "select failed", -1); + goto cleanup; + } + if (i > 0) { + /* get column types */ + sqlite3_reset(stmt); + ncnames = i; + coltypes = sqlite3_malloc(ncnames); + if (!coltypes) { + goto oom; + } + rc = sqlite3_step(stmt); + i = 0; + while (rc == SQLITE_ROW) { + coltypes[i++] = maptype((char *) sqlite3_column_text(stmt, 2)); + rc = sqlite3_step(stmt); + } + if (rc != SQLITE_DONE) { + goto selfail; + } + } else { + /* create new table */ + sqlite3_finalize(stmt); + stmt = 0; + coltypes = sqlite3_malloc(ncnames); + if (!coltypes) { + goto oom; + } + append(&sql, "CREATE TABLE ", 0); + append(&sql, tname, '"'); + append(&sql, "(", 0); + for (i = 0; i < ncnames; i++) { + char colname[64]; + + coltypes[i] = SQLITE_TEXT; + if (useargs) { + char *type = (char *) sqlite3_value_text(argv[i + 6]); + + if (!type) { + goto defcol; + } + append(&sql, type, 0); + while (*type && !strchr(" \t", *type)) { + type++; + } + while (*type && strchr(" \t", *type)) { + type++; + } + coltypes[i] = maptype(type); + } else if (!cnames || !cnames[i]) { +defcol: + sprintf(colname, "column_%d", i + 1); + append(&sql, colname, '"'); + } else if (row1 > 0) { + append(&sql, cnames[i], '"'); + } else if (row1 < 0) { + append(&sql, cnames[i], '"'); + if (i + 6 < argc) { + char *type = (char *) sqlite3_value_text(argv[i + 6]); + + if (type) { + append(&sql, " ", 0); + append(&sql, type, 0); + coltypes[i] = maptype(type); + } + } + } + if (i < ncnames - 1) { + append(&sql, ",", 0); + } + } + append(&sql, ")", 0); + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + if (rc != SQLITE_OK) { + goto prepfail; + } + rc = sqlite3_step(stmt); + if ((rc != SQLITE_DONE) && (rc != SQLITE_OK)) { + sqlite3_result_error(ctx, "create table failed", -1); + goto cleanup; + } + append_free(&sql); + } + sqlite3_finalize(stmt); + stmt = 0; + /* make INSERT statement */ + append(&sql, "INSERT INTO ", 0); + append(&sql, tname, '"'); + append(&sql, " VALUES(", 0); + for (i = 0; i < ncnames; i++) { + append(&sql, (i < ncnames - 1) ? "?," : "?)", 0); + } + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + if (rc != SQLITE_OK) { + goto prepfail; + } + append_free(&sql); + /* import the CSV file */ + row1 = 0; + while (csv_getline(csv, 0) != EOF) { + for (i = 0; i < ncnames; i++) { + char *data = csv_coldata(csv, i); + + rc = process_col(0, stmt, i + 1, data, coltypes[i], convert); + if (rc != SQLITE_OK) { + goto inserr; + } + } + rc = sqlite3_step(stmt); + if ((rc != SQLITE_DONE) && (rc != SQLITE_OK)) { + if ((rc != SQLITE_MISMATCH) && (rc != SQLITE_CONSTRAINT)) { +inserr: + sqlite3_result_error(ctx, "insert failed", -1); + goto cleanup; + } + } else { + row1++; + } + sqlite3_reset(stmt); + } + sqlite3_result_int(ctx, row1); + goto cleanup; +} + +/** + * Module initializer creating SQLite functions and modules + * @param db database pointer + * @result SQLite error code + */ + +#ifndef STANDALONE +static +#endif +int +csv_vtab_init(sqlite3 *db) +{ + sqlite3_create_function(db, "import_csv", -1, SQLITE_UTF8, + (void *) db, csv_import_func, 0, 0); + return sqlite3_create_module(db, "csvtable", &csv_vtab_mod, 0); +} + +#ifndef STANDALONE + +/** + * Initializer for SQLite extension load mechanism. + * @param db SQLite database pointer + * @param errmsg pointer receiving error message + * @param api SQLite API routines + * @result SQLite error code + */ + +int +sqlite3_extension_init(sqlite3 *db, char **errmsg, + const sqlite3_api_routines *api) +{ + SQLITE_EXTENSION_INIT2(api); + return csv_vtab_init(db); +} + +#endif diff --git a/lang/sql/odbc/csvtable.rc b/lang/sql/odbc/csvtable.rc new file mode 100644 index 00000000..202c13ba --- /dev/null +++ b/lang/sql/odbc/csvtable.rc @@ -0,0 +1,48 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite3.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (3) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLite3 Extension SQL CSV Table\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "CSVTABLE\0" + VALUE "LegalCopyright", "Public Domain\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "SQLITE3_MOD_CSVTABLE.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite3 " SQLITE_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqliteodbc.ico" diff --git a/lang/sql/odbc/debian/changelog b/lang/sql/odbc/debian/changelog index 02d05677..becc9c9c 100644 --- a/lang/sql/odbc/debian/changelog +++ b/lang/sql/odbc/debian/changelog @@ -1,6 +1,6 @@ -sqliteodbc (0.93-1) unstable; urgency=low +sqliteodbc (0.996-1) unstable; urgency=low * automatically recreated by configure - -- Christian Werner <chw@ch-werner.de> Tue, 15 Nov 2011 07:45:25 +0100 + -- Christian Werner <chw@ch-werner.de> Sun, 08 Dec 2013 11:38:16 +0100 diff --git a/lang/sql/odbc/doxygen.conf b/lang/sql/odbc/doxygen.conf index 51daad04..fa01aafb 100644 --- a/lang/sql/odbc/doxygen.conf +++ b/lang/sql/odbc/doxygen.conf @@ -43,7 +43,7 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = -INPUT = inst.c adddsn.c sqlite3odbc.h sqlite3odbc.c +INPUT = inst.c adddsn.c sqlite3odbc.h sqlite3odbc.c blobtoxy.c impexp.h impexp.c csvtable.c zipfile.c xpath.c FILE_PATTERNS = RECURSIVE = NO EXCLUDE = @@ -101,12 +101,12 @@ MAN_LINKS = NO GENERATE_XML = NO GENERATE_AUTOGEN_DEF = NO ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = -PREDEFINED = ASYNC +PREDEFINED = HAVE_LOCALECONV=1 HAVE_SQLITE3PROFILE=1 EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = diff --git a/lang/sql/odbc/impexp.c b/lang/sql/odbc/impexp.c index b289ec2f..03428da0 100644 --- a/lang/sql/odbc/impexp.c +++ b/lang/sql/odbc/impexp.c @@ -1,4 +1,9 @@ -/* +/** + * @file impexp.c + * SQLite extension module for importing/exporting + * database information from/to SQL source text and + * export to CSV text. + * * 2007 January 27 * * The author disclaims copyright to this source code. In place of @@ -10,17 +15,15 @@ * ******************************************************************** * - * SQLite extension module for importing/exporting - * database information from/to SQL source text and - * export to CSV text. - * + * <pre> * Usage: * * SQLite function: * SELECT import_sql(filename); * * C function: - * int impexp_import_sql(sqlite3 *db, char *filename); + * int impexp_import_sql(sqlite3 *db, + * char *filename); * * Reads SQL commands from filename and executes them * against the current database. Returns the number @@ -121,22 +124,22 @@ * SELECT export_xml('out.xml', 0, 2, 'TBL_A', 'ROW', 'A'); * -- XML output * <TBL_A> - * <ROW> - * <a TYPE="INTEGER">1</a> - * <b TYPE="REAL">2.1</b> - * </ROW> - * <ROW> - * <a TYPE="INTEGER">3</a> - * <b TYPE="TEXT">foo</b> - * </ROW> - * <ROW> - * <a TYPE="TEXT"></a> - * <b TYPE="NULL"></b> - * </ROW> - * <ROW> - * <a TYPE="BLOB">&x03;</a> - * <b TYPE="TEXT"><blob></b> - * </ROW> + * <ROW> + * <a TYPE="INTEGER">1</a> + * <b TYPE="REAL">2.1</b> + * </ROW> + * <ROW> + * <a TYPE="INTEGER">3</a> + * <b TYPE="TEXT">foo</b> + * </ROW> + * <ROW> + * <a TYPE="TEXT"></a> + * <b TYPE="NULL"></b> + * </ROW> + * <ROW> + * <a TYPE="BLOB">&x03;</a> + * <b TYPE="TEXT">&lt;blob&gt;</b> + * </ROW> * </TBL_A> * * Quoting of XML entities is performed only on the data, @@ -174,6 +177,7 @@ * * On Win32 the filename argument may be specified as NULL in order * to open a system file dialog for interactive filename selection. + * </pre> */ #ifdef STANDALONE @@ -181,7 +185,7 @@ #define sqlite3_api_routines void #else #include <sqlite3ext.h> -SQLITE_EXTENSION_INIT1 +static SQLITE_EXTENSION_INIT1 #endif #include <stdlib.h> @@ -199,16 +203,27 @@ SQLITE_EXTENSION_INIT1 #include "impexp.h" -/* JSON output helper */ +/** + * @typedef struct json_pfs + * @struct json_pfs + * JSON output helper structure + */ typedef struct { - impexp_putc pfunc; - void *parg; + impexp_putc pfunc; /**< function like fputc() */ + void *parg; /**< argument to function */ } json_pfs; static const char space_chars[] = " \f\n\r\t\v"; -#define ISSPACE(c) ((c) && strchr(space_chars, (c)) != 0) +#define ISSPACE(c) ((c) && (strchr(space_chars, (c)) != 0)) + +/** + * Read one line of input into dynamically allocated buffer + * which the caller must free with sqlite3_free() + * @param fin FILE pointer + * @result dynamically allocated input line + */ static char * one_input_line(FILE *fin) @@ -247,7 +262,7 @@ one_input_line(FILE *fin) while (line[n]) { n++; } - if (n > 0 && line[n-1] == '\n') { + if ((n > 0) && (line[n-1] == '\n')) { n--; line[n] = 0; eol = 1; @@ -256,20 +271,32 @@ one_input_line(FILE *fin) tmp = sqlite3_realloc(line, n + 1); if (!tmp) { sqlite3_free(line); - return 0; } return tmp; } +/** + * Test if string ends with a semicolon + * @param str string to be tested + * @param n length of string + * @result true or false + */ + static int ends_with_semicolon(const char *str, int n) { - while (n > 0 && ISSPACE(str[n - 1])) { + while ((n > 0) && ISSPACE(str[n - 1])) { n--; } - return n > 0 && str[n - 1] == ';'; + return (n > 0) && (str[n - 1] == ';'); } +/** + * Test if string contains entirely whitespace or SQL comment + * @param str string to be tested + * @result true or false + */ + static int all_whitespace(const char *str) { @@ -277,9 +304,9 @@ all_whitespace(const char *str) if (ISSPACE(str[0])) { continue; } - if (str[0] == '/' && str[1] == '*') { + if ((str[0] == '/') && (str[1] == '*')) { str += 2; - while (str[0] && (str[0] != '*' || str[1] != '/')) { + while (str[0] && ((str[0] != '*') || (str[1] != '/'))) { str++; } if (!str[0]) { @@ -288,9 +315,9 @@ all_whitespace(const char *str) str++; continue; } - if (str[0] == '-' && str[1] == '-') { + if ((str[0] == '-') && (str[1] == '-')) { str += 2; - while (str[0] && str[0] != '\n') { + while (str[0] && (str[0] != '\n')) { str++; } if (!str[0]) { @@ -303,6 +330,13 @@ all_whitespace(const char *str) return 1; } +/** + * Process contents of FILE pointer as SQL commands + * @param db SQLite database to work on + * @param fin input FILE pointer + * @result number of errors + */ + static int process_input(sqlite3 *db, FILE *fin) { @@ -370,6 +404,18 @@ process_input(sqlite3 *db, FILE *fin) return errors; } +/** + * SQLite function to quote SQLite value depending on optional quote mode + * @param context SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Layout of arguments: + * + * argv[0] - value to be quoted<br> + * argv[1] - value of quote mode (optional)<br> + */ + static void quote_func(sqlite3_context *context, int argc, sqlite3_value **argv) { @@ -393,7 +439,7 @@ quote_func(sqlite3_context *context, int argc, sqlite3_value **argv) } case SQLITE_BLOB: { char *text = 0; - char const *blob = sqlite3_value_blob(argv[0]); + unsigned char *blob = (unsigned char *) sqlite3_value_blob(argv[0]); int nblob = sqlite3_value_bytes(argv[0]); if (2 * nblob + 4 > 1000000000) { @@ -465,6 +511,7 @@ quote_func(sqlite3_context *context, int argc, sqlite3_value **argv) p = sqlite3_malloc(i + n + 3); if (!p) { sqlite3_result_error(context, "out of memory", -1); + return; } p[0] = '\''; for (i = 0, n = 1; arg[i]; i++) { @@ -482,6 +529,13 @@ quote_func(sqlite3_context *context, int argc, sqlite3_value **argv) } } +/** + * SQLite function to quote an SQLite value in CSV format + * @param context SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + static void quote_csv_func(sqlite3_context *context, int argc, sqlite3_value **argv) { @@ -500,7 +554,7 @@ quote_csv_func(sqlite3_context *context, int argc, sqlite3_value **argv) } case SQLITE_BLOB: { char *text = 0; - char const *blob = sqlite3_value_blob(argv[0]); + unsigned char *blob = (unsigned char *) sqlite3_value_blob(argv[0]); int nblob = sqlite3_value_bytes(argv[0]); if (2 * nblob + 4 > 1000000000) { @@ -546,6 +600,7 @@ quote_csv_func(sqlite3_context *context, int argc, sqlite3_value **argv) p = sqlite3_malloc(i + n + 3); if (!p) { sqlite3_result_error(context, "out of memory", -1); + return; } p[0] = '"'; for (i = 0, n = 1; arg[i]; i++) { @@ -563,6 +618,13 @@ quote_csv_func(sqlite3_context *context, int argc, sqlite3_value **argv) } } +/** + * SQLite function to make XML indentation + * @param context SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + static void indent_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) { @@ -580,6 +642,13 @@ indent_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) sqlite3_result_text(context, spaces, n, SQLITE_STATIC); } +/** + * SQLite function to quote a string for XML + * @param context SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + static void quote_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) { @@ -626,7 +695,7 @@ quote_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) } case SQLITE_BLOB: { char *text = 0; - char const *blob = sqlite3_value_blob(argv[0]); + unsigned char *blob = (unsigned char *) sqlite3_value_blob(argv[0]); int nblob = sqlite3_value_bytes(argv[0]); int i, k = 0; @@ -665,9 +734,9 @@ quote_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) return; } for (i = 0, n = 0; arg[i]; i++) { - if (arg[i] == '"' || arg[i] == '\'' || - arg[i] == '<' || arg[i] == '>' || - arg[i] == '&' || arg[i] < ' ') { + if ((arg[i] == '"') || (arg[i] == '\'') || + (arg[i] == '<') || (arg[i] == '>') || + (arg[i] == '&') || (arg[i] < ' ')) { n += 5; } } @@ -723,7 +792,7 @@ quote_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) p[n++] = xdigits[(arg[i] >> 4 ) & 0x0F]; p[n++] = xdigits[arg[i] & 0x0F]; p[n++] = ';'; - } else if (addtype < 0 && arg[i] == ' ') { + } else if (addtype < 0 && (arg[i] == ' ')) { p[n++] = '&'; p[n++] = '#'; p[n++] = 'x'; @@ -742,6 +811,13 @@ quote_xml_func(sqlite3_context *context, int argc, sqlite3_value **argv) } } +/** + * SQLite function to read and process SQL commands from a file + * @param ctx SQLite function context + * @param nargs number of arguments + * @param args argument vector + */ + static void import_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -787,6 +863,8 @@ done: sqlite3_result_int(ctx, sqlite3_changes(db) - changes0); } +/* see doc in impexp.h */ + int impexp_import_sql(sqlite3 *db, char *filename) { @@ -829,16 +907,27 @@ done: return sqlite3_changes(db) - changes0; } +/** + * @typedef DUMP_DATA + * @struct DUMP_DATA + * Structure for dump callback + */ + typedef struct { - sqlite3 *db; - int with_schema; - int quote_mode; - char *where; - int nlines; - int indent; - FILE *out; + sqlite3 *db; /**< SQLite database pointer */ + int with_schema; /**< if true, output schema */ + int quote_mode; /**< mode for quoting data */ + char *where; /**< optional where clause of dump */ + int nlines; /**< counter for output lines */ + int indent; /**< current indent level */ + FILE *out; /**< output file pointer */ } DUMP_DATA; +/** + * Write indentation to dump + * @param dd information structure for dump + */ + static void indent(DUMP_DATA *dd) { @@ -849,10 +938,20 @@ indent(DUMP_DATA *dd) } } +/** + * Execute SQL to dump contents of one table + * @param dd information structure for dump + * @param errp pointer receiving error message + * @param fmt if true, use sqlite3_*printf() on SQL + * @param query SQL text to perform dump of table + * @param ... optional arguments + * @result SQLite error code + */ + static int table_dump(DUMP_DATA *dd, char **errp, int fmt, const char *query, ...) { - sqlite3_stmt *select; + sqlite3_stmt *select = 0; int rc; const char *rest, *q = query; va_list ap; @@ -877,7 +976,7 @@ table_dump(DUMP_DATA *dd, char **errp, int fmt, const char *query, ...) if (fmt) { sqlite3_free((char *) q); } - if (rc != SQLITE_OK || !select) { + if ((rc != SQLITE_OK) || !select) { return rc; } rc = sqlite3_step(select); @@ -905,15 +1004,49 @@ table_dump(DUMP_DATA *dd, char **errp, int fmt, const char *query, ...) return rc; } +/** + * Free dynamically allocated string buffer + * @param in input string pointer + */ + +static void +append_free(char **in) +{ + long *p = (long *) *in; + + if (p) { + p -= 2; + sqlite3_free(p); + *in = 0; + } +} + +/** + * Append a string to dynamically allocated string buffer + * with optional quoting + * @param in input string pointer + * @param append string to append + * @param quote quote character or NUL + * @result new string to be free'd with append_free() + */ + static char * -append(char *in, char const *append, char quote) +append(char **in, char const *append, char quote) { - int len, i; + long *p = (long *) *in; + long len, maxlen, actlen; + int i; + char *pp; int nappend = append ? strlen(append) : 0; - int nin = in ? strlen(in) : 0; - char *tmp; - len = nappend + nin + 1; + if (p) { + p -= 2; + maxlen = p[0]; + actlen = p[1]; + } else { + maxlen = actlen = 0; + } + len = nappend + actlen; if (quote) { len += 2; for (i = 0; i < nappend; i++) { @@ -922,35 +1055,51 @@ append(char *in, char const *append, char quote) } } } else if (!nappend) { - return in; + return *in; } - tmp = (char *) sqlite3_realloc(in, len); - if (!tmp) { - sqlite3_free(in); - return 0; + if (len >= maxlen - 1) { + long *q; + + maxlen = (len + 0x03ff) & (~0x3ff); + q = (long *) sqlite3_realloc(p, maxlen + 1 + 2 * sizeof (long)); + if (!q) { + return 0; + } + if (!p) { + q[1] = 0; + } + p = q; + p[0] = maxlen; + *in = (char *) (p + 2); } - in = tmp; + pp = *in + actlen; if (quote) { - char *p = in + nin; - - *p++ = quote; + *pp++ = quote; for (i = 0; i < nappend; i++) { - *p++ = append[i]; + *pp++ = append[i]; if (append[i] == quote) { - *p++ = quote; + *pp++ = quote; } } - *p++ = quote; - *p++ = '\0'; + *pp++ = quote; + *pp = '\0'; } else { if (nappend) { - memcpy(in + nin, append, nappend); + memcpy(pp, append, nappend); + pp += nappend; + *pp = '\0'; } - in[len - 1] = '\0'; } - return in; + p[1] = pp - *in; + return *in; } +/** + * Quote string for XML output during dump + * @param dd information structure for dump + * @param str string to be output + */ + static void quote_xml_str(DUMP_DATA *dd, char *str) { @@ -989,6 +1138,15 @@ quote_xml_str(DUMP_DATA *dd, char *str) } } +/** + * Callback for sqlite3_exec() to dump one data row + * @param udata information structure for dump + * @param nargs number of columns + * @param args column data + * @param cols column labels + * @result 0 to continue, 1 to abort + */ + static int dump_cb(void *udata, int nargs, char **args, char **cols) { @@ -996,7 +1154,7 @@ dump_cb(void *udata, int nargs, char **args, char **cols) const char *table, *type, *sql; DUMP_DATA *dd = (DUMP_DATA *) udata; - if (nargs != 3 || args == NULL) { + if ((nargs != 3) || (args == NULL)) { return 1; } table = args[0]; @@ -1021,65 +1179,61 @@ dump_cb(void *udata, int nargs, char **args, char **cols) sqlite3_stmt *stmt = 0; char *creat = 0, *table_info = 0; - table_info = append(table_info, "PRAGMA table_info(", 0); - table_info = append(table_info, table, '"'); - table_info = append(table_info, ");", 0); + append(&table_info, "PRAGMA table_info(", 0); + append(&table_info, table, '"'); + append(&table_info, ")", 0); #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 rc = sqlite3_prepare_v2(dd->db, table_info, -1, &stmt, 0); #else rc = sqlite3_prepare(dd->db, table_info, -1, &stmt, 0); #endif - if (table_info) { - sqlite3_free(table_info); - table_info = 0; - } - if (rc != SQLITE_OK || !stmt) { + append_free(&table_info); + if ((rc != SQLITE_OK) || !stmt) { bailout0: - if (creat) { - sqlite3_free(creat); + if (stmt) { + sqlite3_finalize(stmt); } + append_free(&creat); return 1; } - creat = append(creat, table, '"'); - creat = append(creat, "(", 0); + append(&creat, table, '"'); + append(&creat, "(", 0); rc = sqlite3_step(stmt); while (rc == SQLITE_ROW) { const char *p; p = (const char *) sqlite3_column_text(stmt, 1); - creat = append(creat, p, '"'); - creat = append(creat, " ", 0); + append(&creat, p, '"'); + append(&creat, " ", 0); p = (const char *) sqlite3_column_text(stmt, 2); if (p && p[0]) { - creat = append(creat, p, 0); + append(&creat, p, 0); } if (sqlite3_column_int(stmt, 5)) { - creat = append(creat, " PRIMARY KEY", 0); + append(&creat, " PRIMARY KEY", 0); } if (sqlite3_column_int(stmt, 3)) { - creat = append(creat, " NOT NULL", 0); + append(&creat, " NOT NULL", 0); } p = (const char *) sqlite3_column_text(stmt, 4); if (p && p[0]) { - creat = append(creat, " DEFAULT ", 0); - creat = append(creat, p, 0); + append(&creat, " DEFAULT ", 0); + append(&creat, p, 0); } rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { - creat = append(creat, ",", 0); + append(&creat, ",", 0); } } - rc = sqlite3_finalize(stmt); - if (rc != SQLITE_OK) { + if (rc != SQLITE_DONE) { goto bailout0; } - creat = append(creat, ")", 0); + sqlite3_finalize(stmt); + append(&creat, ")", 0); if (creat && fprintf(dd->out, "CREATE TABLE %s;\n", creat) > 0) { dd->nlines++; } - if (creat) { - sqlite3_free(creat); - } + append_free(&creat); } } else { if (dd->with_schema) { @@ -1088,113 +1242,111 @@ bailout0: } } } - if (strcmp(type, "table") == 0 || - (dd->quote_mode < 0 && strcmp(type, "view") == 0)) { + if ((strcmp(type, "table") == 0) || + ((dd->quote_mode < 0) && (strcmp(type, "view") == 0))) { sqlite3_stmt *stmt = 0; - char *select = 0, *hdr = 0, *table_info = 0, *tmp = 0; + char *select = 0, *hdr = 0, *table_info = 0; char buffer[256]; - table_info = append(table_info, "PRAGMA table_info(", 0); - table_info = append(table_info, table, '"'); - table_info = append(table_info, ");", 0); + append(&table_info, "PRAGMA table_info(", 0); + append(&table_info, table, '"'); + append(&table_info, ")", 0); #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 rc = sqlite3_prepare_v2(dd->db, table_info, -1, &stmt, 0); #else rc = sqlite3_prepare(dd->db, table_info, -1, &stmt, 0); #endif - if (rc != SQLITE_OK || !stmt) { + append_free(&table_info); + if ((rc != SQLITE_OK) || !stmt) { bailout1: - if (hdr) { - sqlite3_free(hdr); - } - if (select) { - sqlite3_free(select); - } - if (table_info) { - sqlite3_free(table_info); + if (stmt) { + sqlite3_finalize(stmt); } + append_free(&hdr); + append_free(&select); return 1; } if (dd->quote_mode < -1) { if (dd->where) { - select = append(select, "SELECT ", 0); + append(&select, "SELECT ", 0); sprintf(buffer, "indent_xml(%d)", dd->indent); - select = append(select, buffer, 0); - select = append(select, " || '<' || quote_xml(", 0); - select = append(select, dd->where, '"'); - select = append(select, ",-1) || '>\n' || ", 0); + append(&select, buffer, 0); + append(&select, " || '<' || quote_xml(", 0); + append(&select, dd->where, '"'); + append(&select, ",-1) || '>\n' || ", 0); } else { - select = append(select, "SELECT ", 0); + append(&select, "SELECT ", 0); } } else if (dd->quote_mode < 0) { if (dd->where) { - select = append(select, "SELECT quote_csv(", 0); - select = append(select, dd->where, '"'); - select = append(select, ") || ',' || ", 0); + append(&select, "SELECT quote_csv(", 0); + append(&select, dd->where, '"'); + append(&select, ") || ',' || ", 0); } else { - select = append(select, "SELECT ", 0); + append(&select, "SELECT ", 0); } if (dd->indent) { - hdr = append(hdr, select, 0); + append(&hdr, select, 0); } } else { + char *tmp = 0; + if (dd->with_schema) { - select = append(select, "SELECT 'INSERT INTO ' || ", 0); + append(&select, "SELECT 'INSERT INTO ' || ", 0); } else { - select = append(select, "SELECT 'INSERT OR REPLACE INTO ' || ", - 0); + append(&select, "SELECT 'INSERT OR REPLACE INTO ' || ", 0); } - tmp = append(tmp, table, '"'); + append(&tmp, table, '"'); if (tmp) { - select = append(select, tmp, '\''); - sqlite3_free(tmp); - tmp = 0; + append(&select, tmp, '\''); + append_free(&tmp); } } - if (dd->quote_mode >= 0 && !dd->with_schema) { - select = append(select, " || ' (' || ", 0); + if ((dd->quote_mode >= 0) && !dd->with_schema) { + char *tmp = 0; + + append(&select, " || ' (' || ", 0); rc = sqlite3_step(stmt); while (rc == SQLITE_ROW) { const char *text = (const char *) sqlite3_column_text(stmt, 1); - tmp = append(tmp, text, '"'); + append(&tmp, text, '"'); if (tmp) { - select = append(select, tmp, '\''); - sqlite3_free(tmp); - tmp = 0; + append(&select, tmp, '\''); + append_free(&tmp); } rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { - select = append(select, " || ',' || ", 0); + append(&select, " || ',' || ", 0); } } - rc = sqlite3_reset(stmt); - if (rc != SQLITE_OK) { + if (rc != SQLITE_DONE) { goto bailout1; } - select = append(select, "|| ')'", 0); + sqlite3_reset(stmt); + append(&select, "|| ')'", 0); } - if (dd->quote_mode == -1 && dd->indent) { + if ((dd->quote_mode == -1) && dd->indent) { rc = sqlite3_step(stmt); while (rc == SQLITE_ROW) { const char *text = (const char *) sqlite3_column_text(stmt, 1); - hdr = append(hdr, "quote_csv(", 0); - hdr = append(hdr, text, '"'); + append(&hdr, "quote_csv(", 0); + append(&hdr, text, '"'); rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { - hdr = append(hdr, ") || ',' || ", 0); + append(&hdr, ") || ',' || ", 0); } else { - hdr = append(hdr, ")", 0); + append(&hdr, ")", 0); } } - rc = sqlite3_reset(stmt); - if (rc != SQLITE_OK) { + if (rc != SQLITE_DONE) { goto bailout1; } + sqlite3_reset(stmt); } if (dd->quote_mode >= 0) { - select = append(select, " || ' VALUES(' || ", 0); + append(&select, " || ' VALUES(' || ", 0); } rc = sqlite3_step(stmt); while (rc == SQLITE_ROW) { @@ -1204,18 +1356,18 @@ bailout1: if (dd->quote_mode < -1) { sprintf(buffer, "indent_xml(%d)", dd->indent + 1); - select = append(select, buffer, 0); - select = append(select, "|| '<' || quote_xml(", 0); - select = append(select, text, '\''); - select = append(select, ",-1) || quote_xml(", 0); - select = append(select, text, '"'); - select = append(select, ",1) || '</' || quote_xml(", 0); - select = append(select, text, '\''); - select = append(select, ",-1) || '>\n'", 0); + append(&select, buffer, 0); + append(&select, "|| '<' || quote_xml(", 0); + append(&select, text, '\''); + append(&select, ",-1) || quote_xml(", 0); + append(&select, text, '"'); + append(&select, ",1) || '</' || quote_xml(", 0); + append(&select, text, '\''); + append(&select, ",-1) || '>\n'", 0); } else if (dd->quote_mode < 0) { /* leave out BLOB columns */ - if ((tlen >= 4 && strncasecmp(type, "BLOB", 4) == 0) || - (tlen >= 6 && strncasecmp(type, "BINARY", 6) == 0)) { + if (((tlen >= 4) && (strncasecmp(type, "BLOB", 4) == 0)) || + ((tlen >= 6) && (strncasecmp(type, "BINARY", 6) == 0))) { rc = sqlite3_step(stmt); if (rc != SQLITE_ROW) { tlen = strlen(select); @@ -1225,77 +1377,80 @@ bailout1: } continue; } - select = append(select, "quote_csv(", 0); - select = append(select, text, '"'); + append(&select, "quote_csv(", 0); + append(&select, text, '"'); } else { - select = append(select, "quote_sql(", 0); - select = append(select, text, '"'); + append(&select, "quote_sql(", 0); + append(&select, text, '"'); if (dd->quote_mode) { char mbuf[32]; sprintf(mbuf, ",%d", dd->quote_mode); - select = append(select, mbuf, 0); + append(&select, mbuf, 0); } } rc = sqlite3_step(stmt); if (rc == SQLITE_ROW) { if (dd->quote_mode >= -1) { - select = append(select, ") || ',' || ", 0); + append(&select, ") || ',' || ", 0); } else { - select = append(select, " || ", 0); + append(&select, " || ", 0); } } else { if (dd->quote_mode >= -1) { - select = append(select, ") ", 0); + append(&select, ") ", 0); } else { - select = append(select, " ", 0); + append(&select, " ", 0); } } } - rc = sqlite3_finalize(stmt); - if (rc != SQLITE_OK) { + if (rc != SQLITE_DONE) { goto bailout1; } + sqlite3_finalize(stmt); + stmt = 0; if (dd->quote_mode >= 0) { - select = append(select, "|| ')' FROM ", 0); + append(&select, "|| ')' FROM ", 0); } else { - if (dd->quote_mode < -1 && dd->where) { + if ((dd->quote_mode < -1) && dd->where) { sprintf(buffer, " || indent_xml(%d)", dd->indent); - select = append(select, buffer, 0); - select = append(select, " || '</' || quote_xml(", 0); - select = append(select, dd->where, '"'); - select = append(select, ",-1) || '>\n' FROM ", 0); + append(&select, buffer, 0); + append(&select, " || '</' || quote_xml(", 0); + append(&select, dd->where, '"'); + append(&select, ",-1) || '>\n' FROM ", 0); } else { - select = append(select, "FROM ", 0); + append(&select, "FROM ", 0); } } - select = append(select, table, '"'); - if (dd->quote_mode >= 0 && dd->where) { - select = append(select, " ", 0); - select = append(select, dd->where, 0); - } - if (table_info) { - sqlite3_free(table_info); - table_info = 0; + append(&select, table, '"'); + if ((dd->quote_mode >= 0) && dd->where) { + append(&select, " ", 0); + append(&select, dd->where, 0); } if (hdr) { rc = table_dump(dd, 0, 0, hdr); - sqlite3_free(hdr); + append_free(&hdr); hdr = 0; } rc = table_dump(dd, 0, 0, select); if (rc == SQLITE_CORRUPT) { - select = append(select, " ORDER BY rowid DESC", 0); + append(&select, " ORDER BY rowid DESC", 0); rc = table_dump(dd, 0, 0, select); } - if (select) { - sqlite3_free(select); - select = 0; - } + append_free(&select); } return 0; } +/** + * Execute SQL on sqlite_master table in order to dump data. + * @param dd information structure for dump + * @param errp pointer receiving error message + * @param query SQL for sqlite3_*printf() + * @param ... argument list + * @result SQLite error code + */ + static int schema_dump(DUMP_DATA *dd, char **errp, const char *query, ...) { @@ -1333,6 +1488,13 @@ schema_dump(DUMP_DATA *dd, char **errp, const char *query, ...) return rc; } +/** + * SQLite function for SQL output, see impexp_export_sql + * @param ctx SQLite function context + * @param nargs number of arguments + * @param args argument vector + */ + static void export_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1397,7 +1559,7 @@ export_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) } else { for (i = 2; i < nargs; i += (mode & 2) ? 2 : 1) { dd->where = 0; - if ((mode & 2) && i + 1 < nargs) { + if ((mode & 2) && (i + 1 < nargs)) { dd->where = (char *) sqlite3_value_text(args[i + 1]); } schema_dump(dd, 0, @@ -1423,6 +1585,13 @@ done: sqlite3_result_int(ctx, dd->nlines); } +/** + * SQLite function for CSV output, see impexp_export_csv + * @param ctx SQLite function context + * @param nargs number of arguments + * @param args argument vector + */ + static void export_csv_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1493,7 +1662,7 @@ export_csv_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) if (sqlite3_value_type(args[i + 2]) != SQLITE_NULL) { schema = (char *) sqlite3_value_text(args[i + 2]); } - if (!schema || schema[0] == '\0') { + if (!schema || (schema[0] == '\0')) { schema = "sqlite_master"; } sql = sqlite3_mprintf("SELECT name, type, sql FROM %s" @@ -1510,6 +1679,13 @@ done: sqlite3_result_int(ctx, dd->nlines); } +/** + * SQLite function for XML output, see impexp_export_xml + * @param ctx SQLite function context + * @param nargs number of arguments + * @param args argument vector + */ + static void export_xml_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -1598,7 +1774,7 @@ export_xml_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) if (sqlite3_value_type(args[i + 3]) != SQLITE_NULL) { schema = (char *) sqlite3_value_text(args[i + 3]); } - if (!schema || schema[0] == '\0') { + if (!schema || (schema[0] == '\0')) { schema = "sqlite_master"; } sql = sqlite3_mprintf("SELECT name, type, sql FROM %s" @@ -1622,6 +1798,8 @@ done: sqlite3_result_int(ctx, dd->nlines); } +/* see doc in impexp.h */ + int impexp_export_sql(sqlite3 *db, char *filename, int mode, ...) { @@ -1706,6 +1884,8 @@ done: return dd->nlines; } +/* see doc in impexp.h */ + int impexp_export_csv(sqlite3 *db, char *filename, int hdr, ...) { @@ -1748,7 +1928,7 @@ impexp_export_csv(sqlite3 *db, char *filename, int hdr, ...) #ifdef _WIN32 dd->out = fopen(filename, "wb"); #else - if (hdr < 0 && access(filename, W_OK) == 0) { + if ((hdr < 0) && access(filename, W_OK) == 0) { dd->out = fopen(filename, "a"); dd->indent = 0; } else { @@ -1767,7 +1947,7 @@ impexp_export_csv(sqlite3 *db, char *filename, int hdr, ...) char *sql; dd->where = (prefix && prefix[0]) ? prefix : 0; - if (!schema || schema[0] == '\0') { + if (!schema || (schema[0] == '\0')) { schema = "sqlite_master"; } sql = sqlite3_mprintf("SELECT name, type, sql FROM %s" @@ -1788,6 +1968,8 @@ done: return dd->nlines; } +/* see doc in impexp.h */ + int impexp_export_xml(sqlite3 *db, char *filename, int append, int indnt, char *root, char *item, char *tablename, char *schema) @@ -1804,7 +1986,7 @@ impexp_export_xml(sqlite3 *db, char *filename, int append, int indnt, dd->db = db; dd->where = item; dd->nlines = -1; - dd->indent = indnt > 0 ? indnt : 0; + dd->indent = (indnt > 0) ? indnt : 0; dd->with_schema = 0; dd->quote_mode = -2; #ifdef _WIN32 @@ -1838,7 +2020,7 @@ impexp_export_xml(sqlite3 *db, char *filename, int append, int indnt, quote_xml_str(dd, root); fputs(">\n", dd->out); } - if (!schema || schema[0] == '\0') { + if (!schema || (schema[0] == '\0')) { schema = "sqlite_master"; } sql = sqlite3_mprintf("SELECT name, type, sql FROM %s" @@ -1861,6 +2043,12 @@ done: return dd->nlines; } +/** + * Write string using JSON output function + * @param string string to be written + * @param pfs JSON output function + */ + static void json_pstr(const char *string, json_pfs *pfs) { @@ -1870,6 +2058,12 @@ json_pstr(const char *string, json_pfs *pfs) } } +/** + * Quote and write string using JSON output function + * @param string string to be written + * @param pfs JSON output function + */ + static void json_pstrq(const char *string, json_pfs *pfs) { @@ -1910,7 +2104,7 @@ json_pstrq(const char *string, json_pfs *pfs) pfunc('t', parg); break; default: - if ((*string < ' ' && *string > 0) || *string == 0x7f) { + if (((*string < ' ') && (*string > 0)) || (*string == 0x7f)) { sprintf(buf, "\\u%04x", *string); json_pstr(buf, pfs); } else if (*string < 0) { @@ -1927,8 +2121,8 @@ json_pstrq(const char *string, json_pfs *pfs) uc = c; } } else if (c < 0xf0) { - if ((string[1] & 0xc0) == 0x80 && - (string[2] & 0xc0) == 0x80) { + if (((string[1] & 0xc0) == 0x80) && + ((string[2] & 0xc0) == 0x80)) { uc = ((c & 0x0f) << 12) | ((string[1] & 0x3f) << 6) | (string[2] & 0x3f); string += 2; @@ -1936,9 +2130,9 @@ json_pstrq(const char *string, json_pfs *pfs) uc = c; } } else if (c < 0xf8) { - if ((string[1] & 0xc0) == 0x80 && - (string[2] & 0xc0) == 0x80 && - (string[3] & 0xc0) == 0x80) { + if (((string[1] & 0xc0) == 0x80) && + ((string[2] & 0xc0) == 0x80) && + ((string[3] & 0xc0) == 0x80)) { uc = ((c & 0x03) << 18) | ((string[1] & 0x3f) << 12) | ((string[2] & 0x3f) << 6) | @@ -1948,10 +2142,10 @@ json_pstrq(const char *string, json_pfs *pfs) uc = c; } } else if (c < 0xfc) { - if ((string[1] & 0xc0) == 0x80 && - (string[2] & 0xc0) == 0x80 && - (string[3] & 0xc0) == 0x80 && - (string[4] & 0xc0) == 0x80) { + if (((string[1] & 0xc0) == 0x80) && + ((string[2] & 0xc0) == 0x80) && + ((string[3] & 0xc0) == 0x80) && + ((string[4] & 0xc0) == 0x80)) { uc = ((c & 0x01) << 24) | ((string[1] & 0x3f) << 18) | ((string[2] & 0x3f) << 12) | @@ -1967,12 +2161,14 @@ json_pstrq(const char *string, json_pfs *pfs) } if (uc < 0x10000) { sprintf(buf, "\\u%04lx", uc); - } else { + } else if (uc < 0x100000) { uc -= 0x10000; - sprintf(buf, "\\u%04lx", 0xd800 | (uc & 0x3ff)); + sprintf(buf, "\\u%04lx", 0xd800 | ((uc >> 10) & 0x3ff)); json_pstr(buf, pfs); - sprintf(buf, "\\u%04lx", 0xdc00 | ((uc >> 10) & 0x3ff)); + sprintf(buf, "\\u%04lx", 0xdc00 | (uc & 0x3ff)); + } else { + strcpy(buf, "\\ufffd"); } json_pstr(buf, pfs); } else { @@ -1985,6 +2181,29 @@ json_pstrq(const char *string, json_pfs *pfs) pfunc('"', parg); } +/** + * Conditionally quote and write string using JSON output function + * @param string string to be written + * @param pfs JSON output function + */ + +static void +json_pstrc(const char *string, json_pfs *pfs) +{ + if (*string && strchr(".0123456789-+", *string)) { + json_pstr(string, pfs); + } else { + json_pstrq(string, pfs); + } +} + +/** + * Write a blob as base64 string using JSON output function + * @param blk pointer to blob + * @param len length of blob + * @param pfs JSON output function + */ + static void json_pb64(const unsigned char *blk, int len, json_pfs *pfs) { @@ -2029,6 +2248,15 @@ json_pb64(const unsigned char *blk, int len, json_pfs *pfs) pfunc('"', parg); } +/** + * Execute SQL and write output as JSON + * @param db SQLite database pointer + * @param sql SQL text + * @param pfunc JSON output function + * @param parg argument for output function + * @result SQLite error code + */ + static int json_output(sqlite3 *db, char *sql, impexp_putc pfunc, void *parg) { @@ -2038,39 +2266,40 @@ json_output(sqlite3 *db, char *sql, impexp_putc pfunc, void *parg) pfs->pfunc = pfunc; pfs->parg = parg; - json_pstr("{sql:", pfs); + json_pstr("{\"sql\":", pfs); json_pstrq(sql, pfs); - json_pstr(",results:[", pfs); + json_pstr(",\"results\":[", pfs); do { sqlite3_stmt *stmt; int firstrow = 1, nrows = 0; char buf[256]; ++nresults; - json_pstr(nresults == 1 ? "{" : ",{", pfs); + json_pstr((nresults == 1) ? "{" : ",{", pfs); result = sqlite3_prepare(db, tail, -1, &stmt, &tail); if (result != SQLITE_OK) { doerr: if (nrows == 0) { - json_pstr("columns:null,rows:null,changes:0," - "last_insert_rowid:null,", pfs); + json_pstr("\"columns\":null,\"rows\":null,\"changes\":0," + "\"last_insert_rowid\":null,", pfs); } - json_pstr("error:", pfs); + json_pstr("\"error:\"", pfs); json_pstrq(sqlite3_errmsg(db), pfs); pfunc('}', parg); break; } result = sqlite3_step(stmt); - while (result == SQLITE_ROW || result == SQLITE_DONE) { + while ((result == SQLITE_ROW) || (result == SQLITE_DONE)) { if (firstrow) { for (i = 0; i < sqlite3_column_count(stmt); i++) { char *type; - json_pstr(i == 0 ? "columns:[" : ",", pfs); - json_pstr("{name:", pfs); + + json_pstr((i == 0) ? "\"columns\":[" : ",", pfs); + json_pstr("{\"name\":", pfs); json_pstrq(sqlite3_column_name(stmt, i), pfs); - json_pstr(",decltype:", pfs); + json_pstr(",\"decltype\":", pfs); json_pstrq(sqlite3_column_decltype(stmt, i), pfs); - json_pstr(",type:", pfs); + json_pstr(",\"type\":", pfs); switch (sqlite3_column_type(stmt, i)) { case SQLITE_INTEGER: type = "integer"; @@ -2103,14 +2332,16 @@ doerr: break; } ++nrows; - json_pstr(nrows == 1 ? ",rows:[" : ",", pfs); + json_pstr((nrows == 1) ? ",\"rows\":[" : ",", pfs); for (i = 0; i < sqlite3_column_count(stmt); i++) { - pfunc(i == 0 ? '[' : ',', parg); + pfunc((i == 0) ? '[' : ',', parg); switch (sqlite3_column_type(stmt, i)) { case SQLITE_INTEGER: - case SQLITE_FLOAT: json_pstr((char *) sqlite3_column_text(stmt, i), pfs); break; + case SQLITE_FLOAT: + json_pstrc((char *) sqlite3_column_text(stmt, i), pfs); + break; case SQLITE_BLOB: json_pb64((unsigned char *) sqlite3_column_blob(stmt, i), sqlite3_column_bytes(stmt, i), pfs); @@ -2124,7 +2355,7 @@ doerr: break; } } - json_pstr(i == 0 ? "null]" : "]", pfs); + json_pstr((i == 0) ? "null]" : "]", pfs); result = sqlite3_step(stmt); } if (nrows > 0) { @@ -2135,9 +2366,9 @@ doerr: if (nrows > 0) { sprintf(buf, #ifdef _WIN32 - ",changes:%d,last_insert_rowid:%I64d", + ",\"changes\":%d,\"last_insert_rowid\":%I64d", #else - ",changes:%d,last_insert_rowid:%lld", + ",\"changes\":%d,\"last_insert_rowid\":%lld", #endif sqlite3_changes(db), sqlite3_last_insert_rowid(db)); @@ -2146,23 +2377,30 @@ doerr: goto doerr; } if (nrows == 0) { - json_pstr("columns:null,rows:null", pfs); + json_pstr("\"columns\":null,\"rows\":null", pfs); } sprintf(buf, #ifdef _WIN32 - ",changes:%d,last_insert_rowid:%I64d", + ",\"changes\":%d,\"last_insert_rowid\":%I64d", #else - ",changes:%d,last_insert_rowid:%lld", + ",\"changes\":%d,\"last_insert_rowid\":%lld", #endif sqlite3_changes(db), sqlite3_last_insert_rowid(db)); json_pstr(buf, pfs); - json_pstr(",error:null}", pfs); + json_pstr(",\"error\":null}", pfs); } while (tail && *tail); json_pstr("]}", pfs); return result; } +/** + * SQLite function for JSON output, see impexp_export_json + * @param ctx SQLite function context + * @param nargs number of arguments + * @param args argument vector + */ + static void export_json_func(sqlite3_context *ctx, int nargs, sqlite3_value **args) { @@ -2214,6 +2452,8 @@ done: sqlite3_result_int(ctx, result); } +/* see doc in impexp.h */ + int impexp_export_json(sqlite3 *db, char *sql, impexp_putc pfunc, void *parg) @@ -2221,6 +2461,14 @@ impexp_export_json(sqlite3 *db, char *sql, impexp_putc pfunc, return json_output(db, sql, pfunc, parg); } +/** + * Initializer for SQLite extension load mechanism. + * @param db SQLite database pointer + * @param errmsg pointer receiving error message + * @param api SQLite API routines + * @result SQLite error code + */ + #ifdef STANDALONE static int #else @@ -2267,6 +2515,8 @@ sqlite3_extension_init(sqlite3 *db, char **errmsg, return rc; } +/* see doc in impexp.h */ + int impexp_init(sqlite3 *db) { diff --git a/lang/sql/odbc/impexp.h b/lang/sql/odbc/impexp.h index f7c3aa9a..07910eb5 100644 --- a/lang/sql/odbc/impexp.h +++ b/lang/sql/odbc/impexp.h @@ -1,7 +1,12 @@ #ifndef _IMPEXP_H #define _IMPEXP_H -/* +/** + * @file impexp.h + * SQLite extension module for importing/exporting + * database information from/to SQL source text and + * export to CSV text. + * * 2007 January 27 * * The author disclaims copyright to this source code. In place of @@ -12,178 +17,213 @@ * May you share freely, never taking more than you give. * ******************************************************************** - * - * SQLite extension module for importing/exporting - * database information from/to SQL source text and - * export to CSV text. */ -/* - * impexp_import_sql - * - * Reads SQL commands from filename and executes them - * against the current database. Returns the number - * of changes to the current database. +/** + * Reads SQL commands from filename and executes them + * against the current database. Returns the number + * of changes to the current database. + * @param db SQLite database pointer + * @param filename name of input file + * @result number of changes to database */ int impexp_import_sql(sqlite3 *db, char *filename); -/* - * impexp_export_sql - * - * Writes SQL to filename similar to SQLite's shell - * ".dump" meta command. Mode selects the output format: - * Mode 0 (default): dump schema and data using the - * optional table names following the mode argument. - * Mode 1: dump data only using the optional table - * names following the mode argument. - * Mode 2: dump schema and data using the optional - * table names following the mode argument; each - * table name is followed by a WHERE clause, i.e. - * "mode, table1, where1, table2, where2, ..." - * Mode 3: dump data only, same rules as in mode 2. - * Returns approximate number of lines written or - * -1 when an error occurred. - * +/** + * Writes SQL to filename similar to SQLite's shell + * ".dump" meta command. Mode selects the output format. + * @param db SQLite database pointer + * @param filename name of output file + * @param mode selects output format + * @param ... optional table names or tuples of table name, + * and where-clause depending on mode parameter + * @result approximate number of lines written or + * -1 when an error occurred + * + * Mode 0 (default): dump schema and data using the + * optional table names following the mode argument.<br> + * Mode 1: dump data only using the optional table + * names following the mode argument.<br> + * Mode 2: dump schema and data using the optional + * table names following the mode argument; each + * table name is followed by a WHERE clause, i.e. + * "mode, table1, where1, table2, where2, ..."<br> + * Mode 3: dump data only, same rules as in mode 2.<br> + * + * Other flags in mode: + * + * <pre> * Bit 1 of mode: when 1 dump data only * Bits 8..9 of mode: blob quoting mode + * * 0 default * 256 ORACLE * 512 SQL Server * 768 MySQL + * </pre> */ int impexp_export_sql(sqlite3 *db, char *filename, int mode, ...); -/* - * impexp_export_csv - * - * Writes entire tables as CSV to provided filename. A header - * row is written when the hdr parameter is true. The - * rows are optionally introduced with a column made up of - * the prefix (non-empty string) for the respective table. - * If "schema" is NULL, "sqlite_master" is used, otherwise - * specify e.g. "sqlite_temp_master" for temporary tables or - * "att.sqlite_master" for the attached database "att". - * - * CREATE TABLE A(a,b); - * INSERT INTO A VALUES(1,2); - * INSERT INTO A VALUES(3,'foo'); - * CREATE TABLE B(c); - * INSERT INTO B VALUES('hello'); - * SELECT export_csv('out.csv', 0, 'aa', 'A', NULL, 'bb', 'B', NULL); - * -- CSV output - * "aa",1,2 - * "aa",3,"foo" - * "bb","hello" - * SELECT export_csv('out.csv', 1, 'aa', 'A', NULL, 'bb', 'B', NULL); - * -- CSV output - * "aa","a","b" - * "aa",1,2 - * "aa",3,"foo" - * "bb","c" - * "bb","hello" +/** + * Writes entire tables as CSV to provided filename. A header + * row is written when the hdr parameter is true. The + * rows are optionally introduced with a column made up of + * the prefix (non-empty string) for the respective table. + * If "schema" is NULL, "sqlite_master" is used, otherwise + * specify e.g. "sqlite_temp_master" for temporary tables or + * "att.sqlite_master" for the attached database "att". + * @param db SQLite database pointer + * @param filename name of output file + * @param hdr write header lines when true + * @param ... tuples of prefix, table name, schema name + * @result number of output lines + * + * Example: + * + * <pre> + * CREATE TABLE A(a,b); + * INSERT INTO A VALUES(1,2); + * INSERT INTO A VALUES(3,'foo') + * CREATE TABLE B(c); + * INSERT INTO B VALUES('hello'); + * SELECT export_csv('out.csv', 0, 'aa', 'A', NULL, 'bb', 'B', NULL); + * -- CSV output + * "aa",1,2 + * "aa",3,"foo" + * "bb","hello" + * SELECT export_csv('out.csv', 1, 'aa', 'A', NULL, 'bb', 'B', NULL); + * -- CSV output + * "aa","a","b" + * "aa",1,2 + * "aa",3,"foo" + * "bb","c" + * "bb","hello" + * </pre> */ int impexp_export_csv(sqlite3 *db, char *filename, int hdr, ...); -/* - * impexp_export_xml - * - * Writes a table as simple XML to provided filename. The - * rows are optionally enclosed with the "root" tag, - * the row data is enclosed in "item" tags. If "schema" - * is NULL, "sqlite_master" is used, otherwise specify - * e.g. "sqlite_temp_master" for temporary tables or - * "att.sqlite_master" for the attached database "att". - * - * <item> - * <columnname TYPE="INTEGER|REAL|NULL|TEXT|BLOB">value</columnname> - * ... - * </item> - * - * e.g. - * - * CREATE TABLE A(a,b); - * INSERT INTO A VALUES(1,2.1); - * INSERT INTO A VALUES(3,'foo'); - * INSERT INTO A VALUES('',NULL); - * INSERT INTO A VALUES(X'010203','<blob>'); - * SELECT export_xml('out.xml', 0, 2, 'TBL_A', 'ROW', 'A'); - * -- XML output - * <TBL_A> - * <ROW> - * <a TYPE="INTEGER">1</a> - * <b TYPE="REAL">2.1</b> - * </ROW> - * <ROW> - * <a TYPE="INTEGER">3</a> - * <b TYPE="TEXT">foo</b> - * </ROW> - * <ROW> - * <a TYPE="TEXT"></a> - * <b TYPE="NULL"></b> - * </ROW> - * <ROW> - * <a TYPE="BLOB">&x03;</a> - * <b TYPE="TEXT"><blob></b> - * </ROW> - * </TBL_A> - * - * Quoting of XML entities is performed only on the data, - * not on column names and root/item tags. +/** + * Writes a table as simple XML to provided filename. The + * rows are optionally enclosed with the "root" tag, + * the row data is enclosed in "item" tags. If "schema" + * is NULL, "sqlite_master" is used, otherwise specify + * e.g. "sqlite_temp_master" for temporary tables or + * "att.sqlite_master" for the attached database "att". + * @param db SQLite database pointer + * @param filename name of output file + * @param append if true, append to existing output file + * @param indent number of blanks to indent output + * @param root optional tag use to enclose table output + * @param item tag to use per row + * @param tablename table to be output + * @param schema optional schema or NULL + * @result number of output lines + * + * Layout of an output row: + * <pre> + * <item> + * <columnname TYPE="INTEGER|REAL|NULL|TEXT|BLOB">value</columnname> + * ... + * </item> + * </pre> + * + * Example: + * <pre> + * CREATE TABLE A(a,b); + * INSERT INTO A VALUES(1,2.1); + * INSERT INTO A VALUES(3,'foo'); + * INSERT INTO A VALUES('',NULL); + * INSERT INTO A VALUES(X'010203','<blob>'); + * SELECT export_xml('out.xml', 0, 2, 'TBL_A', 'ROW', 'A'); + * -- XML output + * <TBL_A> + * <ROW> + * <a TYPE="INTEGER">1</a> + * <b TYPE="REAL">2.1</b> + * </ROW> + * <ROW> + * <a TYPE="INTEGER">3</a> + * <b TYPE="TEXT">foo</b> + * </ROW> + * <ROW> + * <a TYPE="TEXT"></a> + * <b TYPE="NULL"></b> + * </ROW> + * <ROW> + * <a TYPE="BLOB">&x03;</a> + * <b TYPE="TEXT">&lt;blob&gt;</b> + * </ROW> + * </TBL_A> + * </pre> + * + * Quoting of XML entities is performed only on the data, + * not on column names and root/item tags. */ int impexp_export_xml(sqlite3 *db, char *filename, int append, int indent, char *root, char *item, char *tablename, char *schema); - -/* - * impexp_export_json - * - * Executes arbitrary SQL statements and formats - * the result in JavaScript Object Notation (JSON). - * The layout of the result is: - * - * object {results, sql} - * results[] object {columns, rows, changes, last_insert_rowid, error} - * columns[] - * object {name, decltype, type } (sqlite3_column_*) - * rows[][] (sqlite3_column_*) - * changes (sqlite3_changes) - * last_insert_rowid (sqlite3_last_insert_rowid) - * error (sqlite3_errmsg) - * sql (SQL text) - * - * For each single SQL statement in "sql" an object in the - * "results" array is produced. - * - * The function pointer for the output function to - * "impexp_export_json" has a signature compatible - * with fputc(3). +/** + * @typedef impexp_putc + * The function pointer for the output function to + * "impexp_export_json" has a signature compatible + * with fputc(3). */ typedef void (*impexp_putc)(int c, void *arg); +/** + * Executes arbitrary SQL statements and formats + * the result in JavaScript Object Notation (JSON). + * @param db SQLite database pointer + * @param sql SQL to be executed + * @param pfunc pointer to output function + * @param parg argument for output function + * @result SQLite error code + * + * The layout of the result output is: + * + * <pre> + * object {results, sql} + * results[] object {columns, rows, changes, last_insert_rowid, error} + * columns[] + * object {name, decltype, type } (sqlite3_column_*) + * rows[][] (sqlite3_column_*) + * changes (sqlite3_changes) + * last_insert_rowid (sqlite3_last_insert_rowid) + * error (sqlite3_errmsg) + * sql (SQL text) + * </pre> + * + * For each single SQL statement in "sql" an object in the + * "results" array is produced. + */ + int impexp_export_json(sqlite3 *db, char *sql, impexp_putc pfunc, void *parg); -/* - * impexp_init +/** + * Registers the SQLite functions + * @param db SQLite database pointer + * @result SQLite error code * - * Registers the SQLite functions + * Registered functions: * - * import_sql(filename) - * export_sql(filename, [mode, tablename, ...]) - * export_csv(filename, hdr, prefix1, tablename1, schema1, ...) - * export_xml(filename, appendflg, indent, - * [root, item, tablename, schema]+) - * export_json(filename, sql) + * <pre> + * import_sql(filename) + * export_sql(filename, [mode, tablename, ...]) + * export_csv(filename, hdr, prefix1, tablename1, schema1, ...) + * export_xml(filename, appendflg, indent, [root, item, tablename, schema]+) + * export_json(filename, sql) + * </pre> * - * On Win32 the filename argument may be specified as NULL in - * order to open a system file dialog for interactive filename - * selection. + * On Win32 the filename argument may be specified as NULL in + * order to open a system file dialog for interactive filename + * selection. */ int impexp_init(sqlite3 *db); diff --git a/lang/sql/odbc/inst.c b/lang/sql/odbc/inst.c index 711a3a2b..60c16ffc 100644 --- a/lang/sql/odbc/inst.c +++ b/lang/sql/odbc/inst.c @@ -2,9 +2,9 @@ * @file inst.c * SQLite ODBC Driver installer/uninstaller for WIN32 * - * $Id: inst.c,v 1.16 2011/11/08 17:29:28 chw Exp chw $ + * $Id: inst.c,v 1.19 2013/01/22 16:37:30 chw Exp chw $ * - * Copyright (c) 2001-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2001-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a @@ -29,47 +29,33 @@ #define SEESTR2 "" #endif -#ifdef _WIN64 -#define NUMDRVS 1 -static char *DriverName[NUMDRVS] = { - "SQLite3 ODBC Driver" SEESTR -}; -static char *DSName[NUMDRVS] = { - "SQLite3 " SEESTR2 "Datasource" -}; -static char *DriverDLL[NUMDRVS] = { - "sqlite3odbc" SEEEXT ".dll" -}; -#ifdef WITH_SQLITE_DLLS -static char *EngineDLL[NUMDRVS] = { - "sqlite3" SEEEXT ".dll" -}; -#endif -#else -#define NUMDRVS 3 +#define NUMDRVS 4 static char *DriverName[NUMDRVS] = { "SQLite ODBC Driver", "SQLite ODBC (UTF-8) Driver", - "SQLite3 ODBC Driver" SEESTR + "SQLite3 ODBC Driver" SEESTR, + "SQLite4 ODBC Driver" }; static char *DSName[NUMDRVS] = { "SQLite Datasource", "SQLite UTF-8 Datasource", - "SQLite3 " SEESTR2 "Datasource" + "SQLite3 " SEESTR2 "Datasource", + "SQLite4 Datasource" }; static char *DriverDLL[NUMDRVS] = { "sqliteodbc.dll", "sqliteodbcu.dll", - "sqlite3odbc" SEEEXT ".dll" + "sqlite3odbc" SEEEXT ".dll", + "sqlite4odbc.dll" }; #ifdef WITH_SQLITE_DLLS static char *EngineDLL[NUMDRVS] = { "sqlite.dll", "sqliteu.dll", - "sqlite3.dll" + "sqlite3.dll", + "sqlite4.dll" }; #endif -#endif static int quiet = 0; static int nosys = 0; diff --git a/lang/sql/odbc/inst.manifest b/lang/sql/odbc/inst.manifest new file mode 100644 index 00000000..312d2ec5 --- /dev/null +++ b/lang/sql/odbc/inst.manifest @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" name="inst" type="win32"/> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="requireAdministrator"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly> diff --git a/lang/sql/odbc/inst.rc b/lang/sql/odbc/inst.rc new file mode 100644 index 00000000..07b4b393 --- /dev/null +++ b/lang/sql/odbc/inst.rc @@ -0,0 +1,62 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite3.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (63) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLiteODBC Driver Installer\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "inst\0" + VALUE "LegalCopyright", "Public Domain\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "inst.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite3 " SQLITE_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqlite.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Manifest +// + +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "inst.manifest" diff --git a/lang/sql/odbc/mf-sqlite.mingw-cross b/lang/sql/odbc/mf-sqlite.mingw-cross new file mode 100644 index 00000000..f35f298a --- /dev/null +++ b/lang/sql/odbc/mf-sqlite.mingw-cross @@ -0,0 +1,146 @@ +#!/usr/make +# +# Makefile for SQLITE, use in conjunction with mingw-cross-build.sh +# since some patches are needed (libshell.c etc.) + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DTHREADSAFE=1 +#THREADSAFE = -DTHREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Leave MEMORY_DEBUG undefined for maximum speed. Use MEMORY_DEBUG=1 +# to check for memory leaks. Use MEMORY_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DMEMORY_DEBUG=2 +#OPTS = -DMEMORY_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_SOUNDEX=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 -march=i386 -mtune=i386 \ + -O2 -DNO_TCL -DNO_OLDNAMES -D_off_t="long long" -D_OFF_T_=1 +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Should the database engine assume text is coded as UTF-8 or iso8859? +# +# ENCODING = UTF8 +ENCODING = ISO8859 + +#### Special libshell target +LIBOBJ += libshell.o + +libshell.o: $(TOP)/src/libshell.c sqlite.h + $(TCCX) -c $(TOP)/src/libshell.c + +# You should not have to change anything below this line +############################################################################### +include $(TOP)/main.mk + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 -lmsvcrt +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 -lmsvcrt +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +sqlite.dll: $(LIBOBJ) + rm -f libsqlite.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite.a -Wl,--strip-all \ + -o sqlite.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 + +sqliteu.dll: $(LIBOBJ) + rm -f libsqlite.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite.a -Wl,--strip-all \ + -o sqliteu.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 diff --git a/lang/sql/odbc/mf-sqlite.mingw64-cross b/lang/sql/odbc/mf-sqlite.mingw64-cross new file mode 100644 index 00000000..807af121 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite.mingw64-cross @@ -0,0 +1,146 @@ +#!/usr/make +# +# Makefile for SQLITE, use in conjunction with mingw-cross-build.sh +# since some patches are needed (libshell.c etc.) + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DTHREADSAFE=1 +#THREADSAFE = -DTHREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Leave MEMORY_DEBUG undefined for maximum speed. Use MEMORY_DEBUG=1 +# to check for memory leaks. Use MEMORY_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DMEMORY_DEBUG=2 +#OPTS = -DMEMORY_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_SOUNDEX=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -O2 -DNO_TCL \ + -DNO_OLDNAMES -D_off_t="long long" -D_OFF_T_=1 +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Should the database engine assume text is coded as UTF-8 or iso8859? +# +# ENCODING = UTF8 +ENCODING = ISO8859 + +#### Special libshell target +LIBOBJ += libshell.o + +libshell.o: $(TOP)/src/libshell.c sqlite.h + $(TCCX) -c $(TOP)/src/libshell.c + +# You should not have to change anything below this line +############################################################################### +include $(TOP)/main.mk + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +sqlite.dll: $(LIBOBJ) + rm -f libsqlite.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite.a -Wl,--strip-all \ + -o sqlite.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 + +sqliteu.dll: $(LIBOBJ) + rm -f libsqlite.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite.a -Wl,--strip-all \ + -o sqliteu.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 diff --git a/lang/sql/odbc/mf-sqlite3.mingw-cross b/lang/sql/odbc/mf-sqlite3.mingw-cross new file mode 100644 index 00000000..b4edb780 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3.mingw-cross @@ -0,0 +1,149 @@ +#!/usr/make +# +# Makefile for SQLITE, use in conjunction with mingw-cross-build.sh +# since some patches are needed (libshell.c etc.) + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = -lcomdlg32 + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_SOUNDEX=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 -march=i386 -mtune=i386 \ + -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +#### Special libshell target +LIBOBJ += libshell.o + +libshell.o: $(TOP)/src/libshell.c sqlite3.h + $(TCCX) -c $(TOP)/src/libshell.c + +SRC += $(TOP)/src/libshell.c + +# You should not have to change anything below this line +############################################################################### +include $(TOP)/main.mk + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 -lmsvcrt +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 -lmsvcrt +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +sqlite3.dll: $(LIBOBJ) + rm -f libsqlite3.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3.a -Wl,--strip-all \ + -o sqlite3.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 diff --git a/lang/sql/odbc/mf-sqlite3.mingw64-cross b/lang/sql/odbc/mf-sqlite3.mingw64-cross new file mode 100644 index 00000000..b6973bf3 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3.mingw64-cross @@ -0,0 +1,148 @@ +#!/usr/make +# +# Makefile for SQLITE, use in conjunction with mingw64-cross-build.sh +# since some patches are needed (libshell.c etc.) + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = -lcomdlg32 + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_SOUNDEX=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +#### Special libshell target +LIBOBJ += libshell.o + +libshell.o: $(TOP)/src/libshell.c sqlite3.h + $(TCCX) -c $(TOP)/src/libshell.c + +SRC += $(TOP)/src/libshell.c + +# You should not have to change anything below this line +############################################################################### +include $(TOP)/main.mk + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +sqlite3.dll: $(LIBOBJ) + rm -f libsqlite3.a + $(TCC) $(CFLAGS) -shared -Wl,--kill-at \ + -Wl,--out-implib,libsqlite3.a -Wl,--strip-all \ + -o sqlite3.dll $(LIBOBJ) $(LMSVCRT) \ + -lcomdlg32 -lkernel32 -luser32 diff --git a/lang/sql/odbc/mf-sqlite3extfunc.mingw-cross b/lang/sql/odbc/mf-sqlite3extfunc.mingw-cross new file mode 100644 index 00000000..fcaab75e --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3extfunc.mingw-cross @@ -0,0 +1,73 @@ +#!/usr/make +# +# Makefile for SQLITE3 extension functions, use in conjunction with +# mingw-cross-build.sh + +#### C Compile and options for use in building executables that +# will run on the target platform. +# +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 -march=i386 -mtune=i386 \ + -O2 -DNO_TCL + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -Isqlite3 -Isqlite3/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr70 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr80 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr90 -lmsvcrt \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr100 -lmsvcrt \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +all: sqlite3_mod_extfunc.$(SO) + +# Rules to build individual files +# +extfunc.o: extfunc.c + $(TCCX) -c extfunc.c + +sqlite3_mod_extfunc.$(SO): extfunc.o + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3extfunc.a -Wl,--strip-all \ + -o sqlite3_mod_extfunc.$(SO) extfunc.o $(LMSVCRT) + +clean: + rm -f extfunc.o libsqlite3extfunc.a sqlite3_mod_extfunc.$(SO) + +semiclean: + rm -f extfunc.o libsqlite3extfunc.a diff --git a/lang/sql/odbc/mf-sqlite3extfunc.mingw64-cross b/lang/sql/odbc/mf-sqlite3extfunc.mingw64-cross new file mode 100644 index 00000000..e620b8ce --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3extfunc.mingw64-cross @@ -0,0 +1,72 @@ +#!/usr/make +# +# Makefile for SQLITE3 extension functions, use in conjunction with +# mingw64-cross-build.sh + +#### C Compile and options for use in building executables that +# will run on the target platform. +# +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -O2 -DNO_TCL + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -Isqlite3 -Isqlite3/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr70 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr80 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr90 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex \ + -lmingw32 -lgcc -lmsvcr100 \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +all: sqlite3_mod_extfunc.$(SO) + +# Rules to build individual files +# +extfunc.o: extfunc.c + $(TCCX) -c extfunc.c + +sqlite3_mod_extfunc.$(SO): extfunc.o + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3extfunc.a -Wl,--strip-all \ + -o sqlite3_mod_extfunc.$(SO) extfunc.o $(LMSVCRT) + +clean: + rm -f extfunc.o libsqlite3extfunc.a sqlite3_mod_extfunc.$(SO) + +semiclean: + rm -f extfunc.o libsqlite3extfunc.a diff --git a/lang/sql/odbc/mf-sqlite3fts.mingw-cross b/lang/sql/odbc/mf-sqlite3fts.mingw-cross new file mode 100644 index 00000000..38dde9b7 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3fts.mingw-cross @@ -0,0 +1,317 @@ +#!/usr/make +# +# Makefile for SQLITE FTS extensions, use in conjunction with +# mingw-cross-build.sh + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_FTS3=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 -march=i386 -mtune=i386 \ + -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll +SHPREFIX = + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP) -I$(TOP)/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 -lmsvcrt +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 -lmsvcrt +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# Object files for FTS1 +# +FTS1OBJ+= fts1.o fts1_hash.o fts1_porter.o fts1_tokenizer1.o + +# Object files for FTS2 +# +FTS2OBJ+= fts2.o fts2_hash.o fts2_porter.o fts2_tokenizer1.o + +# Object files for FTS3 +# +FTS3OBJ+= fts3.o fts3_hash.o fts3_porter.o fts3_tokenizer.o fts3_tokenizer1.o \ + fts3_expr.o fts3_icu.o fts3_snippet.o fts3_write.o fts3_aux.o \ + fts3_tokenize_vtab.o + +# FTS1 source code files. +# +FTS1SRC += \ + $(TOP)/ext/fts1/fts1.c \ + $(TOP)/ext/fts1/fts1.h \ + $(TOP)/ext/fts1/fts1_hash.c \ + $(TOP)/ext/fts1/fts1_hash.h \ + $(TOP)/ext/fts1/fts1_porter.c \ + $(TOP)/ext/fts1/fts1_tokenizer.h \ + $(TOP)/ext/fts1/fts1_tokenizer1.c + +# FTS2 source code files. +# +FTS2SRC += \ + $(TOP)/ext/fts2/fts2.c \ + $(TOP)/ext/fts2/fts2.h \ + $(TOP)/ext/fts2/fts2_hash.c \ + $(TOP)/ext/fts2/fts2_hash.h \ + $(TOP)/ext/fts2/fts2_porter.c \ + $(TOP)/ext/fts2/fts2_tokenizer.h \ + $(TOP)/ext/fts2/fts2_tokenizer1.c + +# FTS3 source code files. +# +FTS3SRC += \ + $(TOP)/ext/fts3/fts3.c \ + $(TOP)/ext/fts3/fts3.h \ + $(TOP)/ext/fts3/fts3_hash.c \ + $(TOP)/ext/fts3/fts3_hash.h \ + $(TOP)/ext/fts3/fts3_porter.c \ + $(TOP)/ext/fts3/fts3_tokenizer.h \ + $(TOP)/ext/fts3/fts3_tokenizer1.c \ + $(TOP)/ext/fts3/fts3_expr.c \ + $(TOP)/ext/fts3/fts3_icu.c \ + $(TOP)/ext/fts3/fts3_snippet.c \ + $(TOP)/ext/fts3/fts3_write.c \ + $(TOP)/ext/fts3/fts3_aux.c \ + $(TOP)/ext/fts3/fts3_tokenize_vtab.c + +# Common headers +HDR += $(TOP)/sqlite3.h \ + $(TOP)/src/sqlite3ext.h + +# Header files used by FTS1 +# +FTS1HDR += \ + $(TOP)/ext/fts1/fts1.h \ + $(TOP)/ext/fts1/fts1_hash.h \ + $(TOP)/ext/fts1/fts1_tokenizer.h + +# Header files used by FTS2 +# +FTS1HDR += \ + $(TOP)/ext/fts2/fts2.h \ + $(TOP)/ext/fts2/fts2_hash.h \ + $(TOP)/ext/fts2/fts2_tokenizer.h + +# Header files used by FTS3 +# +FTS3HDR += \ + $(TOP)/ext/fts3/fts3.h \ + $(TOP)/ext/fts3/fts3_hash.h \ + $(TOP)/ext/fts3/fts3_tokenizer.h \ + $(TOP)/ext/fts3/fts3Int.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +# SQLite 3.4.* +#all: sqlite3_mod_fts1.$(SO) sqlite3_mod_fts2.$(SO) +# SQLite 3.5.* +all: sqlite3_mod_fts3.$(SO) + + +# Rules to build individual files +# +fts1.o: $(TOP)/ext/fts1/fts1.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1.c + +fts1_hash.o: $(TOP)/ext/fts1/fts1_hash.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_hash.c + +fts1_porter.o: $(TOP)/ext/fts1/fts1_porter.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_porter.c + +fts1_tokenizer1.o: $(TOP)/ext/fts1/fts1_tokenizer1.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_tokenizer1.c + +sqlite3_mod_fts1.$(SO): $(FTS1OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts1.a -Wl,--strip-all \ + -o sqlite3_mod_fts1.$(SO) $(FTS1OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + + +fts2.o: $(TOP)/ext/fts2/fts2.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2.c + +fts2_hash.o: $(TOP)/ext/fts2/fts2_hash.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_hash.c + +fts2_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_porter.c + +fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_tokenizer1.c + +sqlite3_mod_fts2.$(SO): $(FTS2OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts2.a -Wl,--strip-all \ + -o sqlite3_mod_fts2.$(SO) $(FTS2OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + + +fts3.o: $(TOP)/ext/fts3/fts3.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3.c + +fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_hash.c + +fts3_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_porter.c + +fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenizer.c + +fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenizer1.c + +fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_expr.c + +fts3_icu.o: $(TOP)/ext/fts3/fts3_icu.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_icu.c + +fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_write.c + +fts3_snippet.o: $(TOP)/ext/fts3/fts3_snippet.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_snippet.c + +fts3_aux.o: $(TOP)/ext/fts3/fts3_aux.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_aux.c + +fts3_tokenize_vtab.o: $(TOP)/ext/fts3/fts3_tokenize_vtab.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenize_vtab.c + +sqlite3_mod_fts3.$(SO): $(FTS3OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts3.a -Wl,--strip-all \ + -o sqlite3_mod_fts3.$(SO) $(FTS3OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + + +clean: + rm -f $(FTS1OBJ) libsqlite3fts1.a sqlite3_mod_fts1.$(SO) + rm -f $(FTS2OBJ) libsqlite3fts2.a sqlite3_mod_fts2.$(SO) + rm -f $(FTS3OBJ) libsqlite3fts3.a sqlite3_mod_fts3.$(SO) diff --git a/lang/sql/odbc/mf-sqlite3fts.mingw64-cross b/lang/sql/odbc/mf-sqlite3fts.mingw64-cross new file mode 100644 index 00000000..d95ce620 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3fts.mingw64-cross @@ -0,0 +1,315 @@ +#!/usr/make +# +# Makefile for SQLITE FTS extensions, use in conjunction with +# mingw64-cross-build.sh + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_FTS3=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll +SHPREFIX = + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP) -I$(TOP)/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# Object files for FTS1 +# +FTS1OBJ+= fts1.o fts1_hash.o fts1_porter.o fts1_tokenizer1.o + +# Object files for FTS2 +# +FTS2OBJ+= fts2.o fts2_hash.o fts2_porter.o fts2_tokenizer1.o + +# Object files for FTS3 +# +FTS3OBJ+= fts3.o fts3_hash.o fts3_porter.o fts3_tokenizer.o fts3_tokenizer1.o \ + fts3_expr.o fts3_icu.o fts3_snippet.o fts3_write.o fts3_aux.o \ + fts3_tokenize_vtab.o + +# FTS1 source code files. +# +FTS1SRC += \ + $(TOP)/ext/fts1/fts1.c \ + $(TOP)/ext/fts1/fts1.h \ + $(TOP)/ext/fts1/fts1_hash.c \ + $(TOP)/ext/fts1/fts1_hash.h \ + $(TOP)/ext/fts1/fts1_porter.c \ + $(TOP)/ext/fts1/fts1_tokenizer.h \ + $(TOP)/ext/fts1/fts1_tokenizer1.c + +# FTS2 source code files. +# +FTS2SRC += \ + $(TOP)/ext/fts2/fts2.c \ + $(TOP)/ext/fts2/fts2.h \ + $(TOP)/ext/fts2/fts2_hash.c \ + $(TOP)/ext/fts2/fts2_hash.h \ + $(TOP)/ext/fts2/fts2_porter.c \ + $(TOP)/ext/fts2/fts2_tokenizer.h \ + $(TOP)/ext/fts2/fts2_tokenizer1.c + +# FTS3 source code files. +# +FTS3SRC += \ + $(TOP)/ext/fts3/fts3.c \ + $(TOP)/ext/fts3/fts3.h \ + $(TOP)/ext/fts3/fts3_hash.c \ + $(TOP)/ext/fts3/fts3_hash.h \ + $(TOP)/ext/fts3/fts3_porter.c \ + $(TOP)/ext/fts3/fts3_tokenizer.h \ + $(TOP)/ext/fts3/fts3_tokenizer1.c \ + $(TOP)/ext/fts3/fts3_expr.c \ + $(TOP)/ext/fts3/fts3_icu.c \ + $(TOP)/ext/fts3/fts3_snippet.c \ + $(TOP)/ext/fts3/fts3_write.c \ + $(TOP)/ext/fts3/fts3_aux.c \ + $(TOP)/ext/fts3/fts3_tokenize_vtab.c + +# Common headers +HDR += $(TOP)/sqlite3.h \ + $(TOP)/src/sqlite3ext.h + +# Header files used by FTS1 +# +FTS1HDR += \ + $(TOP)/ext/fts1/fts1.h \ + $(TOP)/ext/fts1/fts1_hash.h \ + $(TOP)/ext/fts1/fts1_tokenizer.h + +# Header files used by FTS2 +# +FTS1HDR += \ + $(TOP)/ext/fts2/fts2.h \ + $(TOP)/ext/fts2/fts2_hash.h \ + $(TOP)/ext/fts2/fts2_tokenizer.h + +# Header files used by FTS3 +# +FTS3HDR += \ + $(TOP)/ext/fts3/fts3.h \ + $(TOP)/ext/fts3/fts3_hash.h \ + $(TOP)/ext/fts3/fts3_tokenizer.h \ + $(TOP)/ext/fts3/fts3Int.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +# SQLite 3.4.* +#all: sqlite3_mod_fts1.$(SO) sqlite3_mod_fts2.$(SO) +# SQLite 3.5.* +all: sqlite3_mod_fts3.$(SO) + + +# Rules to build individual files +# +fts1.o: $(TOP)/ext/fts1/fts1.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1.c + +fts1_hash.o: $(TOP)/ext/fts1/fts1_hash.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_hash.c + +fts1_porter.o: $(TOP)/ext/fts1/fts1_porter.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_porter.c + +fts1_tokenizer1.o: $(TOP)/ext/fts1/fts1_tokenizer1.c $(FTS1HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts1/fts1_tokenizer1.c + +sqlite3_mod_fts1.$(SO): $(FTS1OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts1.a -Wl,--strip-all \ + -o sqlite3_mod_fts1.$(SO) $(FTS1OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + + +fts2.o: $(TOP)/ext/fts2/fts2.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2.c + +fts2_hash.o: $(TOP)/ext/fts2/fts2_hash.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_hash.c + +fts2_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_porter.c + +fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(FTS2HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts2/fts2_tokenizer1.c + +sqlite3_mod_fts2.$(SO): $(FTS2OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts2.a -Wl,--strip-all \ + -o sqlite3_mod_fts2.$(SO) $(FTS2OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + + +fts3.o: $(TOP)/ext/fts3/fts3.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3.c + +fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_hash.c + +fts3_porter.o: $(TOP)/ext/fts2/fts2_porter.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_porter.c + +fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenizer.c + +fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenizer1.c + +fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_expr.c + +fts3_icu.o: $(TOP)/ext/fts3/fts3_icu.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_icu.c + +fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_write.c + +fts3_snippet.o: $(TOP)/ext/fts3/fts3_snippet.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_snippet.c + +fts3_aux.o: $(TOP)/ext/fts3/fts3_aux.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_aux.c + +fts3_tokenize_vtab.o: $(TOP)/ext/fts3/fts3_tokenize_vtab.c $(FTS3HDR) $(HDR) + $(TCCX) -c $(TOP)/ext/fts3/fts3_tokenize_vtab.c + +sqlite3_mod_fts3.$(SO): $(FTS3OBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3fts3.a -Wl,--strip-all \ + -o sqlite3_mod_fts3.$(SO) $(FTS3OBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +clean: + rm -f $(FTS1OBJ) libsqlite3fts1.a sqlite3_mod_fts1.$(SO) + rm -f $(FTS2OBJ) libsqlite3fts2.a sqlite3_mod_fts2.$(SO) + rm -f $(FTS3OBJ) libsqlite3fts3.a sqlite3_mod_fts3.$(SO) diff --git a/lang/sql/odbc/mf-sqlite3rtree.mingw-cross b/lang/sql/odbc/mf-sqlite3rtree.mingw-cross new file mode 100644 index 00000000..b1ddcd24 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3rtree.mingw-cross @@ -0,0 +1,182 @@ +#!/usr/make +# +# Makefile for SQLITE rtree extensions, use in conjunction with +# mingw-cross-build.sh + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_RTREE=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 -march=i386 -mtune=i386 \ + -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll +SHPREFIX = + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP) -I$(TOP)/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 -lmsvcrt +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 -lmsvcrt +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# Object files for rtree +# +RTREEOBJ+= rtree.o + +# rtree source code files. +# +RTREESRC += \ + $(TOP)/ext/rtree/rtree.h \ + $(TOP)/ext/rtree/rtree.c + +# Common headers +HDR += $(TOP)/sqlite3.h \ + $(TOP)/src/sqlite3ext.h + +# Header files used by rtree +# +RTREEHDR += \ + $(TOP)/ext/rtree/rtree.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +all: sqlite3_mod_rtree.$(SO) + +# Rules to build individual files +# + +sqlite3_mod_rtree.$(SO): $(RTREEOBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3rtree.a -Wl,--strip-all \ + -o sqlite3_mod_rtree.$(SO) $(RTREEOBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +rtree.o: $(TOP)/ext/rtree/rtree.c $(RTREEHDR) $(HDR) + $(TCCX) -c $(TOP)/ext/rtree/rtree.c + +clean: + rm -f $(RTREEOBJ) libsqlite3rtree.a sqlite3_mod_rtree.$(SO) + diff --git a/lang/sql/odbc/mf-sqlite3rtree.mingw64-cross b/lang/sql/odbc/mf-sqlite3rtree.mingw64-cross new file mode 100644 index 00000000..f192a2e5 --- /dev/null +++ b/lang/sql/odbc/mf-sqlite3rtree.mingw64-cross @@ -0,0 +1,181 @@ +#!/usr/make +# +# Makefile for SQLITE rtree extensions, use in conjunction with +# mingw64-cross-build.sh + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. +# +TOP = ../sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. +# +BCC = gcc -g -O2 +#BCC = /opt/ancic/bin/c89 -0 + +#### If the target operating system supports the "usleep()" system +# call, then define the HAVE_USLEEP macro for all C modules. +# +USLEEP = +#USLEEP = -DHAVE_USLEEP=1 + +#### If you want the SQLite library to be safe for use within a +# multi-threaded program, then define the following macro +# appropriately: +# +THREADSAFE = -DSQLITE_THREADSAFE=1 +#THREADSAFE = -DSQLITE_THREADSAFE=0 + +#### Specify any extra linker options needed to make the library +# thread safe +# +#THREADLIB = -lpthread +THREADLIB = + +#### Specify any extra libraries needed to access required functions. +# +#TLIBS = -lrt # fdatasync on Solaris 8 +TLIBS = + +#### Leave SQLITE_DEBUG undefined for maximum speed. Use SQLITE_DEBUG=1 +# to check for memory leaks. Use SQLITE_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 +# +#OPTS = -DSQLITE_DEBUG=2 +#OPTS = -DSQLITE_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_ENABLE_RTREE=1 \ + -DSQLITE_ENABLE_MEMSYS5=1 \ + -DWIN32=1 -DSQLITE_OS_WIN=1 $(THREADSAFE) +#OPTS += -DHAVE_FDATASYNC=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. +# +EXE = .exe +#EXE = + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. +# +#TCC = gcc -O6 +#TCC = gcc -g -O0 -Wall +#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage +TCC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -O2 -DNO_TCL +#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive + +#### Tools used to build a static library. +# +#AR = ar cr +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar cr +#RANLIB = ranlib +RANLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ranlib + +# MKSHLIB = gcc -shared +# SO = so +# SHPREFIX = lib +MKSHLIB = /opt/mingw64/bin/x86_64-w64-mingw32-ar -shared +SO = dll +SHPREFIX = + +#### Extra compiler options needed for programs that use the TCL library. +# +TCL_FLAGS = +#TCL_FLAGS = -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.4linux +#TCL_FLAGS = -I/home/drh/tcltk/8.4win -DSTATIC_BUILD=1 +#TCL_FLAGS = -I/home/drh/tcltk/8.3hpux + +#### Linker options needed to link against the TCL library. +# +LIBTCL = +#LIBTCL = -ltcl -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4linux/libtcl8.4g.a -lm -ldl +#LIBTCL = /home/drh/tcltk/8.4win/libtcl84s.a -lmsvcrt +#LIBTCL = /home/drh/tcltk/8.3hpux/libtcl8.3.a -ldld -lm -lc + +#### Compiler options needed for programs that use the readline() library. +# +READLINE_FLAGS = +#READLINE_FLAGS = -DHAVE_READLINE=1 -I/usr/include/readline + +#### Linker options needed by programs using readline() must link against. +# +LIBREADLINE = +#LIBREADLINE = -static -lreadline -ltermcap + +#### Which "awk" program provides nawk compatibilty +# +# NAWK = nawk +NAWK = awk + +# This is how we compile +# +TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP) -I$(TOP)/src + +ifeq ($(MSVCRT),70) + TCCX += -D__MSVCRT_VERSION=0x0700 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr70 +endif +ifeq ($(MSVCRT),80) + TCCX += -D__MSVCRT_VERSION=0x0800 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr80 +endif +ifeq ($(MSVCRT),90) + TCCX += -D__MSVCRT_VERSION=0x0900 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr90 +endif +ifeq ($(MSVCRT),100) + TCCX += -D__MSVCRT_VERSION=0x0A00 + LMSVCRT = -nodefaultlibs -lmingw32 -lgcc_eh -lmoldname -lmingwex -lgcc -lmsvcr100 +endif +ifeq ($(LMSVCRT),) + LMSVCRT = -lmsvcrt +endif + +# Object files for rtree +# +RTREEOBJ+= rtree.o + +# rtree source code files. +# +RTREESRC += \ + $(TOP)/ext/rtree/rtree.h \ + $(TOP)/ext/rtree/rtree.c + +# Common headers +HDR += $(TOP)/sqlite3.h \ + $(TOP)/src/sqlite3ext.h + +# Header files used by rtree +# +RTREEHDR += \ + $(TOP)/ext/rtree/rtree.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. +# +all: sqlite3_mod_rtree.$(SO) + +# Rules to build individual files +# + +sqlite3_mod_rtree.$(SO): $(RTREEOBJ) + $(TCCX) -shared -Wl,--kill-at \ + -Wl,-out-implib,libsqlite3rtree.a -Wl,--strip-all \ + -o sqlite3_mod_rtree.$(SO) $(RTREEOBJ) $(LMSVCRT) \ + -lgdi32 -lcomdlg32 \ + -ladvapi32 -lshell32 -luser32 -lkernel32 + +rtree.o: $(TOP)/ext/rtree/rtree.c $(RTREEHDR) $(HDR) + $(TCCX) -c $(TOP)/ext/rtree/rtree.c + +clean: + rm -f $(RTREEOBJ) libsqlite3rtree.a sqlite3_mod_rtree.$(SO) + diff --git a/lang/sql/odbc/mf-zlib.mingw-cross b/lang/sql/odbc/mf-zlib.mingw-cross new file mode 100644 index 00000000..b549c519 --- /dev/null +++ b/lang/sql/odbc/mf-zlib.mingw-cross @@ -0,0 +1,42 @@ +STATICLIB = libz.a + +CC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc -m32 +CFLAGS = -march=i386 -mtune=i386 -O2 + +LD = $(CC) +LDFLAGS = + +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar +ARFLAGS = rcs + +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ + gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o \ + zutil.o + +all: $(STATICLIB) + +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +$(STATICLIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +clean: + -$(RM) $(STATICLIB) + -$(RM) *.o + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: crc32.h zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +gzclose.o: zlib.h zconf.h gzguts.h +gzlib.o: zlib.h zconf.h gzguts.h +gzread.o: zlib.h zconf.h gzguts.h +gzwrite.o: zlib.h zconf.h gzguts.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/lang/sql/odbc/mf-zlib.mingw64-cross b/lang/sql/odbc/mf-zlib.mingw64-cross new file mode 100644 index 00000000..b57a47a3 --- /dev/null +++ b/lang/sql/odbc/mf-zlib.mingw64-cross @@ -0,0 +1,42 @@ +STATICLIB = libz.a + +CC = /opt/mingw64/bin/x86_64-w64-mingw32-gcc +CFLAGS = -O2 + +LD = $(CC) +LDFLAGS = + +AR = /opt/mingw64/bin/x86_64-w64-mingw32-ar +ARFLAGS = rcs + +OBJS = adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o \ + gzwrite.o infback.o inffast.o inflate.o inftrees.o trees.o uncompr.o \ + zutil.o + +all: $(STATICLIB) + +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +$(STATICLIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +clean: + -$(RM) $(STATICLIB) + -$(RM) *.o + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: crc32.h zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +gzclose.o: zlib.h zconf.h gzguts.h +gzlib.o: zlib.h zconf.h gzguts.h +gzread.o: zlib.h zconf.h gzguts.h +gzwrite.o: zlib.h zconf.h gzguts.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/lang/sql/odbc/mingw-cross-build.sh b/lang/sql/odbc/mingw-cross-build.sh new file mode 100644 index 00000000..89788e52 --- /dev/null +++ b/lang/sql/odbc/mingw-cross-build.sh @@ -0,0 +1,1719 @@ +#!/bin/sh +# +# Build script for cross compiling and packaging SQLite +# ODBC drivers and tools for Win32 using MinGW and NSIS. +# Tested on Fedora Core 3/5/8, Debian Etch, RHEL 5/6 +# +# Cross toolchain and NSIS for Linux/i386/x86_64 can be fetched from +# http://www.ch-werner.de/xtools/crossmingw64-0.3-1.i386.rpm +# http://www.ch-werner.de/xtools/crossmingw64-0.3-1.x86_64.rpm +# http://www.ch-werner.de/xtools/nsis-2.37-1.i386.rpm +# or +# http://www.ch-werner.de/xtools/crossmingw64-0.3.i386.tar.bz2 +# http://www.ch-werner.de/xtools/crossmingw64-0.3.x86_64.tar.bz2 +# http://www.ch-werner.de/xtools/nsis-2.37-1_i386.tar.gz + +# Some aspects of the build process can be controlled by shell variables: +# +# NO_SQLITE2=1 omit building SQLite 2 and drivers for it +# NO_TCCEXT=1 omit building TCC extension +# WITH_SOURCES=1 add source directory to NSIS installer +# SQLITE_DLLS=1 build and package drivers with SQLite 2/3 DLLs +# SQLITE_DLLS=2 build drivers with refs to SQLite 2/3 DLLs +# SQLite3 driver can use System.Data.SQlite.dll + +set -e + +VER2=2.8.17 +VER3=3.8.2 +VER3X=3080200 +VERZ=1.2.7 +TCCVER=0.9.26 + +nov2=false +if test -n "$NO_SQLITE2" ; then + nov2=true + ADD_NSIS="-DWITHOUT_SQLITE2" +fi + +notcc=false +if test -n "$NO_TCCEXT" ; then + notcc=true + ADD_NSIS="$ADD_NSIS -DWITHOUT_TCCEXT" +else + export SQLITE_TCC_DLL="sqlite+tcc.dll" +fi + +if test -f "$WITH_SEE" ; then + export SEEEXT=see + ADD_NSIS="$ADD_NSIS -DWITH_SEE=$SEEEXT" + if test "$SQLITE_DLLS" = "2" ; then + SQLITE_DLLS=1 + fi +fi + +if test "$SQLITE_DLLS" = "2" ; then + # turn on -DSQLITE_DYNLOAD in sqlite3odbc.c + export ADD_CFLAGS="-DWITHOUT_SHELL=1 -DWITH_SQLITE_DLLS=2" + ADD_NSIS="$ADD_NSIS -DWITHOUT_SQLITE3_EXE" +elif test -n "$SQLITE_DLLS" ; then + export ADD_CFLAGS="-DWITHOUT_SHELL=1 -DWITH_SQLITE_DLLS=1" + export SQLITE3_DLL="-Lsqlite3 -lsqlite3" + export SQLITE3_EXE="sqlite3.exe" + ADD_NSIS="$ADD_NSIS -DWITH_SQLITE_DLLS" +else + export SQLITE3_A10N_O="sqlite3a10n.o" + export SQLITE3_EXE="sqlite3.exe" +fi + +if test -n "$WITH_SOURCES" ; then + ADD_NSIS="$ADD_NSIS -DWITH_SOURCES" +fi + +echo "==================" +echo "Preparing zlib ..." +echo "==================" +test -r zlib-${VERZ}.tar.gz || \ + wget -c http://zlib.net/zlib-${VERZ}.tar.gz || exit 1 +rm -rf zlib-${VERZ} +tar xzf zlib-${VERZ}.tar.gz +ln -sf zlib-${VERZ} zlib + +echo "====================" +echo "Preparing sqlite ..." +echo "====================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || test -r sqlite-${VER2}.tar.gz || \ + wget -c https://www.sqlite.org/sqlite-${VER2}.tar.gz \ + --no-check-certificate +$nov2 || test -r sqlite-${VER2}.tar.gz || exit 1 + +$nov2 || rm -f sqlite +$nov2 || tar xzf sqlite-${VER2}.tar.gz +$nov2 || ln -sf sqlite-${VER2} sqlite + +# enable sqlite_encode_binary et.al. +$nov2 || patch sqlite/main.mk <<'EOD' +--- sqlite.orig/main.mk 2005-04-24 00:43:23.000000000 +0200 ++++ sqlite/main.mk 2006-03-16 14:29:55.000000000 +0100 +@@ -55,7 +55,7 @@ + # Object files for the SQLite library. + # + LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ +- expr.o func.o hash.o insert.o \ ++ expr.o func.o hash.o insert.o encode.o \ + main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \ + select.o table.o tokenize.o trigger.o update.o util.o \ + vacuum.o vdbe.o vdbeaux.o where.o tclsqlite.o +EOD + +# display encoding +$nov2 || patch sqlite/src/shell.c <<'EOD' +--- sqlite.orig/src/shell.c 2005-04-24 00:43:22.000000000 +0200 ++++ sqlite/src/shell.c 2006-05-23 08:22:01.000000000 +0200 +@@ -1180,6 +1180,7 @@ + " -separator 'x' set output field separator (|)\n" + " -nullvalue 'text' set text string for NULL values\n" + " -version show SQLite version\n" ++ " -encoding show SQLite encoding\n" + " -help show this text, also show dot-commands\n" + ; + static void usage(int showDetail){ +@@ -1297,7 +1298,10 @@ + }else if( strcmp(z,"-echo")==0 ){ + data.echoOn = 1; + }else if( strcmp(z,"-version")==0 ){ +- printf("%s\n", sqlite_version); ++ printf("%s\n", sqlite_libversion()); ++ return 1; ++ }else if( strcmp(z,"-encoding")==0 ){ ++ printf("%s\n", sqlite_libencoding()); + return 1; + }else if( strcmp(z,"-help")==0 ){ + usage(1); +@@ -1330,9 +1334,9 @@ + char *zHome; + char *zHistory = 0; + printf( +- "SQLite version %s\n" ++ "SQLite version %s encoding %s\n" + "Enter \".help\" for instructions\n", +- sqlite_version ++ sqlite_libversion(), sqlite_libencoding() + ); + zHome = find_home_dir(); + if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){ +EOD + +# use open file dialog when no database name given +# need to link with -lcomdlg32 when enabled +true || patch sqlite/src/shell.c <<'EOD' +--- sqlite.orig/src/shell.c 2006-07-23 11:18:13.000000000 +0200 ++++ sqlite/src/shell.c 2006-07-23 11:30:26.000000000 +0200 +@@ -20,6 +20,10 @@ + #include "sqlite.h" + #include <ctype.h> + ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) + # include <signal.h> + # include <pwd.h> +@@ -1246,6 +1250,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + data.zDbFilename = ":memory:"; + } + if( i<argc ){ +EOD + +# same but new module libshell.c +$nov2 || patch sqlite/main.mk <<'EOD' +--- sqlite.orig/main.mk 2007-01-10 19:30:52.000000000 +0100 ++++ sqlite/main.mk 2007-01-10 19:33:39.000000000 +0100 +@@ -54,7 +54,7 @@ + + # Object files for the SQLite library. + # +-LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ ++LIBOBJ += attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ + expr.o func.o hash.o insert.o encode.o \ + main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \ + select.o table.o tokenize.o trigger.o update.o util.o \ +EOD +$nov2 || cp -p sqlite/src/shell.c sqlite/src/libshell.c +$nov2 || patch sqlite/src/libshell.c <<'EOD' +--- sqlite.orig/src/libshell.c 2007-01-10 19:13:01.000000000 +0100 ++++ sqlite/src/libshell.c 2007-01-10 19:25:56.000000000 +0100 +@@ -20,6 +20,10 @@ + #include "sqlite.h" + #include <ctype.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) + # include <signal.h> + # include <pwd.h> +@@ -1205,7 +1209,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1246,6 +1250,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + data.zDbFilename = ":memory:"; + } + if( i<argc ){ +EOD + +$nov2 || rm -f sqlite/src/minshell.c +$nov2 || touch sqlite/src/minshell.c +$nov2 || patch sqlite/src/minshell.c <<'EOD' +--- sqlite.orig/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 ++++ sqlite/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 +@@ -0,0 +1,20 @@ ++/* ++** 2001 September 15 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++** ++************************************************************************* ++** This file contains code to implement the "sqlite" command line ++** utility for accessing SQLite databases. ++*/ ++ ++int sqlite_main(int argc, char **argv); ++ ++int main(int argc, char **argv){ ++ return sqlite_main(argc, argv); ++} +EOD + +echo "=====================" +echo "Preparing sqlite3 ..." +echo "=====================" +test -r sqlite-src-${VER3X}.zip || \ + wget -c https://www.sqlite.org/2013/sqlite-src-${VER3X}.zip \ + --no-check-certificate +test -r sqlite-src-${VER3X}.zip || exit 1 +test -r extension-functions.c || + wget -O extension-functions.c -c \ + 'https://www.sqlite.org/contrib/download/extension-functions.c?get=25' \ + --no-check-certificate +if test -r extension-functions.c ; then + cp extension-functions.c extfunc.c + patch < extfunc.patch +fi +test -r extfunc.c || exit 1 + +rm -f sqlite3 +rm -rf sqlite-src-${VER3X} +unzip sqlite-src-${VER3X}.zip +ln -sf sqlite-src-${VER3X} sqlite3 + +patch sqlite3/main.mk <<'EOD' +--- sqlite3.orig/main.mk 2007-03-31 14:32:21.000000000 +0200 ++++ sqlite3/main.mk 2007-04-02 11:04:50.000000000 +0200 +@@ -67,7 +67,7 @@ + + # All of the source code files. + # +-SRC = \ ++SRC += \ + $(TOP)/src/alter.c \ + $(TOP)/src/analyze.c \ + $(TOP)/src/attach.c \ +EOD + +# use open file dialog when no database name given +# need to link with -lcomdlg32 when enabled +true || patch sqlite3/src/shell.c <<'EOD' +--- sqlite3.orig/src/shell.c 2006-06-06 14:32:21.000000000 +0200 ++++ sqlite3/src/shell.c 2006-07-23 11:04:50.000000000 +0200 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) + # include <signal.h> + # include <pwd.h> +@@ -1676,6 +1676,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD +# SQLite 3.5.1 Win32 mutex fix +test "$VER3" != "3.5.1" || patch sqlite3/src/mutex_w32.c <<'EOD' +--- sqlite3.orig/src/mutex_w32.c 2007-08-30 14:10:30 ++++ sqlite3/src/mutex_w32.c 2007-09-04 22:31:3 +@@ -141,6 +141,12 @@ + p->nRef++; + } + int sqlite3_mutex_try(sqlite3_mutex *p){ ++ /* The TryEnterCriticalSection() interface is not available on all ++ ** windows systems. Since sqlite3_mutex_try() is only used as an ++ ** optimization, we can skip it on windows. */ ++ return SQLITE_BUSY; ++ ++#if 0 /* Not Available */ + int rc; + assert( p ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || sqlite3_mutex_notheld(p) ); +@@ -152,6 +158,7 @@ + rc = SQLITE_BUSY; + } + return rc; ++#endif + } + + /* + +EOD + +# same but new module libshell.c +cp -p sqlite3/src/shell.c sqlite3/src/libshell.c +test "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2007-01-08 23:40:05.000000000 +0100 ++++ sqlite3/src/libshell.c 2007-01-10 18:35:43.000000000 +0100 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) + # include <signal.h> + # include <pwd.h> +@@ -1774,7 +1778,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1816,6 +1820,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +test "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2007-01-08 23:40:05.000000000 +0100 ++++ sqlite3/src/libshell.c 2007-01-10 18:35:43.000000000 +0100 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) + # include <signal.h> + # include <pwd.h> +@@ -1774,7 +1778,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1816,6 +1820,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +test "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + -o "$VER3" = "3.7.16" -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" \ + -o "$VER3" = "3.7.17" -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" \ + -o "$VER3" = "3.8.2" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2012-12-12 14:42:10.000000000 +0100 ++++ sqlite3/src/libshell.c 2012-12-13 12:14:57.000000000 +0100 +@@ -36,6 +36,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++#include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) + # include <signal.h> + # if !defined(__RTP__) && !defined(_WRS_KERNEL) +@@ -2894,7 +2898,7 @@ + return argv[i]; + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -2996,6 +3000,17 @@ + } + } + if( data.zDbFilename==0 ){ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +rm -f sqlite3/src/minshell.c +touch sqlite3/src/minshell.c +patch sqlite3/src/minshell.c <<'EOD' +--- sqlite3.orig/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 ++++ sqlite3/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 +@@ -0,0 +1,20 @@ ++/* ++** 2001 September 15 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++** ++************************************************************************* ++** This file contains code to implement the "sqlite" command line ++** utility for accessing SQLite databases. ++*/ ++ ++int sqlite3_main(int argc, char **argv); ++ ++int main(int argc, char **argv){ ++ return sqlite3_main(argc, argv); ++} +EOD + +# amalgamation: add libshell.c +test "$VER3" != "3.5.6" && test -r sqlite3/tool/mksqlite3c.tcl && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/tool/mksqlite3c.tcl 2007-04-02 14:20:10.000000000 +0200 ++++ sqlite3/tool/mksqlite3c.tcl 2007-04-03 09:42:03.000000000 +0200 +@@ -194,6 +194,7 @@ + where.c + + parse.c ++ libshell.c + + tokenize.c + complete.c +EOD +test "$VER3" = "3.5.6" && test -r sqlite3/tool/mksqlite3c.tcl && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/tool/mksqlite3c.tcl 2007-04-02 14:20:10.000000000 +0200 ++++ sqlite3/tool/mksqlite3c.tcl 2007-04-03 09:42:03.000000000 +0200 +@@ -200,6 +200,7 @@ + + main.c + ++ libshell.c + fts3.c + fts3_hash.c + fts3_porter.c +EOD + +# patch: parse foreign key constraints on virtual tables +test "$VER3" != "3.6.15" -a "$VER3" != "3.6.16" -a "$VER3" != "3.6.17" \ + -a "$VER3" != "3.6.18" -a "$VER3" != "3.6.19" -a "$VER3" != "3.6.20" \ + -a "$VER3" != "3.6.21" -a "$VER3" != "3.6.22" -a "$VER3" != "3.6.23" \ + -a "$VER3" != "3.6.23.1" -a "$VER3" != "3.7.0" -a "$VER3" != "3.7.0.1" \ + -a "$VER3" != "3.7.1" -a "$VER3" != "3.7.2" -a "$VER3" != "3.7.3" \ + -a "$VER3" != "3.7.4" -a "$VER3" != "3.7.5" -a "$VER3" != "3.7.6" \ + -a "$VER3" != "3.7.6.1" -a "$VER3" != "3.7.6.2" -a "$VER3" != "3.7.6.3" \ + -a "$VER3" != "3.7.7" -a "$VER3" != "3.7.7.1" -a "$VER3" != "3.7.8" \ + -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" -a "$VER3" != "3.7.11" \ + -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" -a "$VER3" != "3.7.13" \ + -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +diff -u sqlite3.orig/src/build.c sqlite3/src/build.c +--- sqlite3.orig/src/build.c 2007-01-09 14:53:04.000000000 +0100 ++++ sqlite3/src/build.c 2007-01-30 08:14:41.000000000 +0100 +@@ -2063,7 +2063,7 @@ + char *z; + + assert( pTo!=0 ); +- if( p==0 || pParse->nErr || IN_DECLARE_VTAB ) goto fk_end; ++ if( p==0 || pParse->nErr ) goto fk_end; + if( pFromCol==0 ){ + int iCol = p->nCol-1; + if( iCol<0 ) goto fk_end; +diff -u sqlite3.orig/src/pragma.c sqlite3/src/pragma.c +--- sqlite3.orig/src/pragma.c 2007-01-27 03:24:56.000000000 +0100 ++++ sqlite3/src/pragma.c 2007-01-30 09:19:30.000000000 +0100 +@@ -589,6 +589,9 @@ + pTab = sqlite3FindTable(db, zRight, zDb); + if( pTab ){ + v = sqlite3GetVdbe(pParse); ++#ifndef SQLITE_OMIT_VIRTUAL_TABLE ++ if( pTab->pVtab ) sqlite3ViewGetColumnNames(pParse, pTab); ++#endif + pFK = pTab->pFKey; + if( pFK ){ + int i = 0; +diff -u sqlite3.orig/src/vtab.c sqlite3/src/vtab.c +--- sqlite3.orig/src/vtab.c 2007-01-09 15:01:14.000000000 +0100 ++++ sqlite3/src/vtab.c 2007-01-30 08:23:22.000000000 +0100 +@@ -540,6 +540,9 @@ + int rc = SQLITE_OK; + Table *pTab = db->pVTab; + char *zErr = 0; ++#ifndef SQLITE_OMIT_FOREIGN_KEYS ++ FKey *pFKey; ++#endif + + sqlite3_mutex_enter(db->mutex); + pTab = db->pVTab; +@@ -568,6 +571,15 @@ + } + sParse.declareVtab = 0; + ++#ifndef SQLITE_OMIT_FOREIGN_KEYS ++ assert( pTab->pFKey==0 ); ++ pTab->pFKey = sParse.pNewTable->pFKey; ++ sParse.pNewTable->pFKey = 0; ++ for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ pFKey->pFrom=pTab; ++ } ++#endif ++ + if( sParse.pVdbe ){ + sqlite3VdbeFinalize(sParse.pVdbe); + } +EOD + +# patch: re-enable NO_TCL in tclsqlite.c (3.3.15) +patch -d sqlite3 -p1 <<'EOD' +diff -u sqlite3.orig/src/tclsqlite.c sqlite3/src/tclsqlite.c +--- sqlite3.orig/src/tclsqlite.c 2007-04-06 17:02:14.000000000 +0200 ++++ sqlite3/src/tclsqlite.c 2007-04-10 07:47:49.000000000 +0200 +@@ -14,6 +14,7 @@ + ** + ** $Id: mingw-cross-build.sh,v 1.86 2013/12/08 10:37:28 chw Exp chw $ + */ ++#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ + #include "tcl.h" + + /* +@@ -2264,3 +2265,5 @@ + return 0; + } + #endif /* TCLSH */ ++ ++#endif /* !defined(NO_TCL) */ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 < 3.4.0 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2007-04-11 19:52:04.000000000 +0200 ++++ sqlite3/src/os_win.c 2007-05-08 06:57:06.000000000 +0200 +@@ -1237,8 +1237,8 @@ + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; +- if( pFile->locktype==NO_LOCK +- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) ++ if( locktype==SHARED_LOCK ++ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -1289,6 +1289,18 @@ + newLocktype = EXCLUSIVE_LOCK; + }else{ + OSTRACE2("error-code = %d\n", GetLastError()); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -1362,6 +1374,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 >= 3.5.4 && <= 3.6.10 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2007-12-13 22:38:58.000000000 +0100 ++++ sqlite3/src/os_win.c 2008-01-18 10:01:48.000000000 +0100 +@@ -855,8 +855,8 @@ + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; +- if( pFile->locktype==NO_LOCK +- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) ++ if( locktype==SHARED_LOCK ++ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -907,7 +907,18 @@ + newLocktype = EXCLUSIVE_LOCK; + }else{ + OSTRACE2("error-code = %d\n", GetLastError()); +- getReadLock(pFile); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -982,6 +993,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 >= 3.6.11 && < 3.7.0 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2009-02-15 14:07:09.000000000 +0100 ++++ sqlite3/src/os_win.c 2009-02-20 16:39:48.000000000 +0100 +@@ -922,7 +922,7 @@ + newLocktype = pFile->locktype; + if( (pFile->locktype==NO_LOCK) + || ( (locktype==EXCLUSIVE_LOCK) +- && (pFile->locktype==RESERVED_LOCK)) ++ && (pFile->locktype<RESERVED_LOCK)) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -981,7 +981,18 @@ + }else{ + error = GetLastError(); + OSTRACE2("error-code = %d\n", error); +- getReadLock(pFile); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -1057,6 +1068,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: compile fix for FTS3 as extension module +test "$VER3" != "3.6.21" -a "$VER3" != "3.6.22" -a "$VER3" != "3.6.23" \ + -a "$VER3" != "3.6.23.1" -a "$VER3" != "3.7.0" -a "$VER3" != "3.7.0.1" \ + -a "$VER3" != "3.7.1" -a "$VER3" != "3.7.2" -a "$VER3" != "3.7.3" \ + -a "$VER3" != "3.7.4" -a "$VER3" != "3.7.5" -a "$VER3" != "3.7.6" \ + -a "$VER3" != "3.7.6.1" -a "$VER3" != "3.7.6.2" -a "$VER3" != "3.7.6.3" \ + -a "$VER3" != "3.7.7" -a "$VER3" != "3.7.7.1" -a "$VER3" != "3.7.8" \ + -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" -a "$VER3" != "3.7.11" \ + -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" -a "$VER3" != "3.7.13" \ + -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2008-02-02 17:24:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2008-03-16 11:29:02.000000000 +0100 +@@ -274,10 +274,6 @@ + + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stdio.h> +@@ -6389,7 +6385,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD +test "$VER3" = "3.6.21" -o "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" \ + -o "$VER3" = "3.6.23.1" -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2008-02-02 17:24:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2008-03-16 11:29:02.000000000 +0100 +@@ -274,10 +274,6 @@ + + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include "fts3Int.h" + + #include <assert.h> +@@ -6389,7 +6385,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD +patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_porter.c 2008-02-01 16:40:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_porter.c 2008-03-16 11:34:50.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + /* +--- sqlite3.orig/ext/fts3/fts3_tokenizer1.c 2007-11-23 18:31:18.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer1.c 2008-03-16 11:35:37.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + typedef struct simple_tokenizer { +EOD +test "$VER3" != "3.7.8" -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" \ + -a "$VER3" != "3.7.11" -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" \ + -a "$VER3" != "3.7.13" -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" \ + -a "$VER3" != "3.7.15" -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" \ + -a "$VER3" != "3.7.16" -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" \ + -a "$VER3" != "3.7.17" -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" \ + -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_hash.c 2007-11-24 01:41:52.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_hash.c 2008-03-16 11:39:57.000000000 +0100 +@@ -29,6 +29,11 @@ + #include <stdlib.h> + #include <string.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "sqlite3.h" + #include "fts3_hash.h" +EOD +test "$VER3" = "3.6.21" && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2009-12-03 20:39:06.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-01-05 07:59:27.000000000 +0100 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +EOD +test "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" -o "$VER3" = "3.6.23.1" \ + -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2010-01-05 09:42:19.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-01-05 09:55:25.000000000 +0100 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +@@ -2226,7 +2230,7 @@ + + if( !zVal ){ + return SQLITE_NOMEM; +- }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ ++ }else if( nVal==8 && 0==strnicmp(zVal, "optimize", 8) ){ + rc = fts3SegmentMerge(p, -1); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; +@@ -2234,10 +2238,10 @@ + sqlite3Fts3PendingTermsClear(p); + } + #ifdef SQLITE_TEST +- }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ ++ }else if( nVal>9 && 0==strnicmp(zVal, "nodesize=", 9) ){ + p->nNodeSize = atoi(&zVal[9]); + rc = SQLITE_OK; +- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ ++ }else if( nVal>11 && 0==strnicmp(zVal, "maxpending=", 9) ){ + p->nMaxPendingData = atoi(&zVal[11]); + rc = SQLITE_OK; + #endif +EOD + +test "$VER3" = "3.7.6" -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" \ + -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2011-04-12 11:44:56.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_write.c 2011-04-13 08:00:51.000000000 +0200 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +@@ -2450,7 +2454,7 @@ + + if( !zVal ){ + return SQLITE_NOMEM; +- }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ ++ }else if( nVal==8 && 0==strnicmp(zVal, "optimize", 8) ){ + rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; +@@ -2458,10 +2462,10 @@ + sqlite3Fts3PendingTermsClear(p); + } + #ifdef SQLITE_TEST +- }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ ++ }else if( nVal>9 && 0==strnicmp(zVal, "nodesize=", 9) ){ + p->nNodeSize = atoi(&zVal[9]); + rc = SQLITE_OK; +- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ ++ }else if( nVal>11 && 0==strnicmp(zVal, "maxpending=", 9) ){ + p->nMaxPendingData = atoi(&zVal[11]); + rc = SQLITE_OK; + #endif +--- sqlite3.orig/ext/fts3/fts3_aux.c 2011-04-12 11:44:56.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_aux.c 2011-04-13 08:16:17.000000000 +0200 +@@ -15,6 +15,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD + +test "$VER3" = "3.6.21" -o "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" \ + -o "$VER3" = "3.6.23.1" -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_snippet.c 2010-01-05 08:03:51.000000000 +0100 +@@ -14,6 +14,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <ctype.h> +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2010-01-05 08:06:10.000000000 +0100 +@@ -17,6 +17,11 @@ + */ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +@@ -445,7 +450,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = (int)strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = (int)((zInput - z) + nStr + 1); +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2009-12-07 17:38:46.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2010-01-05 08:12:50.000000000 +0100 +@@ -27,7 +27,7 @@ + + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + + #include "fts3Int.h" +@@ -166,7 +166,7 @@ + if( !z ){ + zCopy = sqlite3_mprintf("simple"); + }else{ +- if( sqlite3_strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){ ++ if( strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){ + return SQLITE_OK; + } + zCopy = sqlite3_mprintf("%s", &z[8]); +EOD + +test "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_snippet.c 2010-01-05 08:03:51.000000000 +0100 +@@ -14,6 +14,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <ctype.h> +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2009-12-07 17:38:46.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2010-01-05 08:12:50.000000000 +0100 +@@ -27,7 +27,7 @@ + + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + + #include "fts3Int.h" +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2010-01-05 08:06:10.000000000 +0100 +@@ -17,6 +17,11 @@ + */ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +@@ -445,7 +450,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = (int)strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = (int)((zInput - z) + nStr + 1); +--- sqlite3.orig/ext/fts3/fts3.c 2010-12-07 16:14:36.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2010-12-16 11:59:02.000000000 +0100 +@@ -702,8 +698,8 @@ + sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ + + assert( strlen(argv[0])==4 ); +- assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) +- || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) ++ assert( (strnicmp(argv[0], "fts4", 4)==0 && isFts4) ++ || (strnicmp(argv[0], "fts3", 4)==0 && !isFts4) + ); + + nDb = (int)strlen(argv[1]) + 1; +@@ -732,7 +728,7 @@ + /* Check if this is a tokenizer specification */ + if( !pTokenizer + && strlen(z)>8 +- && 0==sqlite3_strnicmp(z, "tokenize", 8) ++ && 0==strnicmp(z, "tokenize", 8) + && 0==sqlite3Fts3IsIdChar(z[8]) + ){ + rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr); +@@ -744,8 +740,8 @@ + rc = SQLITE_NOMEM; + goto fts3_init_out; + } +- if( nKey==9 && 0==sqlite3_strnicmp(z, "matchinfo", 9) ){ +- if( strlen(zVal)==4 && 0==sqlite3_strnicmp(zVal, "fts3", 4) ){ ++ if( nKey==9 && 0==strnicmp(z, "matchinfo", 9) ){ ++ if( strlen(zVal)==4 && 0==strnicmp(zVal, "fts3", 4) ){ + bNoDocsize = 1; + }else{ + *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); +--- sqlite3.orig/ext/fts3/fts3_write.c 2010-12-16 12:08:45.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-12-16 12:48:30.000000000 +0100 +@@ -868,16 +868,16 @@ + assert( pnBlob); + + if( p->pSegments ){ +- rc = sqlite3_blob_reopen(p->pSegments, iBlockid); +- }else{ +- if( 0==p->zSegmentsTbl ){ +- p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); +- if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; +- } +- rc = sqlite3_blob_open( +- p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments +- ); ++ sqlite3_blob_close(p->pSegments); ++ p->pSegments = 0; + } ++ if( 0==p->zSegmentsTbl ){ ++ p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); ++ if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; ++ } ++ rc = sqlite3_blob_open( ++ p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments ++ ); + + if( rc==SQLITE_OK ){ + int nByte = sqlite3_blob_bytes(p->pSegments); +EOD + +# patch: FTS3 again, for SQLite3 >= 3.6.8 +test "$VER3" = "3.6.8" -o "$VER3" = "3.6.9" -o "$VER3" = "3.6.10" \ + -o "$VER3" = "3.6.11" -o "$VER3" = "3.6.12" -o "$VER3" = "3.6.13" \ + -o "$VER3" = "3.6.14" -o "$VER3" = "3.6.14.1" -o "$VER3" = "3.6.14.2" \ + -o "$VER3" = "3.6.15" -o "$VER3" = "3.6.16" -o "$VER3" = "3.6.17" \ + -o "$VER3" = "3.6.18" -o "$VER3" = "3.6.19" -o "$VER3" = "3.6.20" && + patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-01-01 15:06:13.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2009-01-14 09:55:13.000000000 +0100 +@@ -57,6 +57,12 @@ + #define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 + + #include "fts3_expr.h" ++ ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "sqlite3.h" + #include <ctype.h> + #include <string.h> +EOD +test "$VER3" = "3.6.17" -o "$VER3" = "3.6.18" -o "$VER3" = "3.6.19" \ + -o "$VER3" = "3.6.20" && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-01-01 15:06:13.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2009-01-14 09:55:13.000000000 +0100 +@@ -428,7 +428,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = ((zInput - z) + nStr + 1); +EOD +# patch: compile fix for rtree as extension module +test "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/rtree/rtree.c 2008-07-16 16:43:35.000000000 +0200 ++++ sqlite3/ext/rtree/rtree.c 2008-07-17 08:59:53.000000000 +0200 +@@ -2812,7 +2812,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD + +# patch: compile fix for rtree as extension module +test "$VER3" = "3.7.3" -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" \ + -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + -o "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/rtree/rtree.c 2010-10-16 10:53:54.000000000 +0200 ++++ sqlite3/ext/rtree/rtree.c 2010-10-16 11:12:32.000000000 +0200 +@@ -3193,6 +3193,8 @@ + return rc; + } + ++#ifdef SQLITE_CORE ++ + /* + ** A version of sqlite3_free() that can be used as a callback. This is used + ** in two places - as the destructor for the blob value returned by the +@@ -3257,6 +3259,8 @@ + ); + } + ++#endif ++ + #ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, +EOD + +# patch: .read shell command +test "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + -o "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/shell.c 2011-05-19 15:34:57.000000000 +0200 ++++ sqlite3/src/shell.c 2011-06-09 13:36:13.000000000 +0200 +@@ -1957,6 +1957,7 @@ + }else{ + rc = process_input(p, alt); + fclose(alt); ++ if( rc ) rc = 1; + } + }else + +EOD + +# patch: FTS3 for 3.7.7 plus missing APIs in sqlite3ext.h/loadext.c +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_aux.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_aux.c 2011-06-25 06:44:08.000000000 +0200 +@@ -14,6 +14,10 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3.c 2011-06-25 06:48:49.000000000 +0200 +@@ -295,10 +295,6 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stddef.h> +@@ -3136,7 +3132,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD +test "$VER3" = "3.7.8" -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" \ + -o "$VER3" = "3.7.11" -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" \ + -o "$VER3" = "3.7.13" -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + -o "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + -o "$VER3" = "3.7.16" -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" \ + -o "$VER3" = "3.7.17" -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" \ + -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2011-09-19 20:46:52.000000000 +0200 ++++ sqlite3/ext/fts3/fts3.c 2011-09-20 09:47:40.000000000 +0200 +@@ -295,10 +295,6 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stddef.h> +@@ -4826,7 +4822,7 @@ + } + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + /* + ** Initialize API pointer table, if required. + */ +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_expr.c 2011-06-25 06:47:00.000000000 +0200 +@@ -18,6 +18,11 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_snippet.c 2011-06-25 06:45:47.000000000 +0200 +@@ -13,7 +13,10 @@ + + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) +- ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2011-06-25 06:50:19.000000000 +0200 +@@ -25,7 +25,7 @@ + */ + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + #include "fts3Int.h" + +--- sqlite3.orig/ext/fts3/fts3_write.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_write.c 2011-06-25 06:45:05.000000000 +0200 +@@ -20,6 +20,10 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +--- sqlite3.orig/src/sqlite3ext.h 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/src/sqlite3ext.h 2011-06-25 07:28:06.000000000 +0200 +@@ -212,6 +212,9 @@ + int (*wal_autocheckpoint)(sqlite3*,int); + int (*wal_checkpoint)(sqlite3*,const char*); + void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); ++ int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); ++ int (*vtab_config)(sqlite3*,int op,...); ++ int (*vtab_on_conflict)(sqlite3*); + }; + + /* +@@ -412,6 +415,9 @@ + #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint + #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint + #define sqlite3_wal_hook sqlite3_api->wal_hook ++#define sqlite3_blob_reopen sqlite3_api->blob_reopen ++#define sqlite3_vtab_config sqlite3_api->vtab_config ++#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict + #endif /* SQLITE_CORE */ + + #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; +--- sqlite3.orig/src/loadext.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/src/loadext.c 2011-06-25 07:29:59.000000000 +0200 +@@ -84,6 +84,8 @@ + # define sqlite3_create_module 0 + # define sqlite3_create_module_v2 0 + # define sqlite3_declare_vtab 0 ++# define sqlite3_vtab_config 0 ++# define sqlite3_vtab_on_conflict 0 + #endif + + #ifdef SQLITE_OMIT_SHARED_CACHE +@@ -107,6 +109,7 @@ + #define sqlite3_blob_open 0 + #define sqlite3_blob_read 0 + #define sqlite3_blob_write 0 ++#define sqlite3_blob_reopen 0 + #endif + + /* +@@ -372,6 +375,18 @@ + 0, + 0, + #endif ++#ifndef SQLITE_OMIT_INCRBLOB ++ sqlite3_blob_reopen, ++#else ++ 0, ++#endif ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ sqlite3_vtab_config, ++ sqlite3_vtab_on_conflict, ++#else ++ 0, ++ 0, ++#endif + }; + + /* +EOD +test "$VER3" = "3.7.11" -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" \ + -o "$VER3" = "3.7.13" -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + -o "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/sqlite3ext.h 2012-03-22 20:13:33.000000000 +0100 ++++ sqlite3/src/sqlite3ext.h 2012-03-22 20:13:57.000000000 +0100 +@@ -236,6 +236,7 @@ + int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); + int (*vtab_config)(sqlite3*,int op,...); + int (*vtab_on_conflict)(sqlite3*); ++ int (*stricmp)(const char*,const char*); + }; + + /* +@@ -439,6 +440,7 @@ + #define sqlite3_blob_reopen sqlite3_api->blob_reopen + #define sqlite3_vtab_config sqlite3_api->vtab_config + #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict ++#define sqlite3_stricmp sqlite3_api->stricmp + #endif /* SQLITE_CORE */ + + #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; +--- sqlite3.orig/src/loadext.c 2012-03-20 15:20:13.000000000 +0100 ++++ sqlite3/src/loadext.c 2012-03-22 20:16:24.000000000 +0100 +@@ -378,6 +378,7 @@ + sqlite3_blob_reopen, + sqlite3_vtab_config, + sqlite3_vtab_on_conflict, ++ sqlite3_stricmp, + }; + + /* +EOD + +test "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/loadext.c 2013-09-16 06:56:48.000000000 +0200 ++++ sqlite3/src/loadext.c 2013-09-16 06:58:14.000000000 +0200 +@@ -495,7 +495,8 @@ + memcpy(zAltEntry, "sqlite3_", 8); + for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} + iFile++; +- if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; ++ if( sqlite3_strnicmp(zFile+iFile, "sqlite3_mod_", 12)==0 ) iFile += 12; ++ else if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; + for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ + if( sqlite3Isalpha(c) ){ + zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; +EOD + +echo "====================" +echo "Preparing TinyCC ..." +echo "====================" +( $notcc && echo '*** skipped (NO_TCCEXT)' ) || true +$notcc || test -r tcc-${TCCVER}.tar.bz2 || \ + wget -c http://download.savannah.gnu.org/releases/tinycc/tcc-${TCCVER}.tar.bz2 +$notcc || test -r tcc-${TCCVER}.tar.bz2 || exit 1 + +$notcc || rm -rf tcc tcc-${TCCVER} +$notcc || tar xjf tcc-${TCCVER}.tar.bz2 +$notcc || ln -sf tcc-${TCCVER} tcc +$notcc || patch -d tcc -p1 < tcc-${TCCVER}.patch + +echo "========================" +echo "Cleanup before build ..." +echo "========================" +make -f Makefile.mingw-cross clean +$notv2 || make -C sqlite -f ../mf-sqlite.mingw-cross clean +make -C sqlite3 -f ../mf-sqlite3.mingw-cross clean +make -C sqlite3 -f ../mf-sqlite3fts.mingw-cross clean +make -C sqlite3 -f ../mf-sqlite3rtree.mingw-cross clean +make -f mf-sqlite3extfunc.mingw-cross clean + +echo "=============================" +echo "Building SQLite 2 ... ISO8859" +echo "=============================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -C sqlite -f ../mf-sqlite.mingw-cross all +if test -n "$SQLITE_DLLS" ; then + $nov2 || make -C sqlite -f ../mf-sqlite.mingw-cross sqlite.dll +fi + +echo "=================" +echo "Building zlib ..." +echo "=================" + +make -C zlib -f ../mf-zlib.mingw-cross all + +echo "=====================" +echo "Building SQLite 3 ..." +echo "=====================" +make -C sqlite3 -f ../mf-sqlite3.mingw-cross all +test -r sqlite3/tool/mksqlite3c.tcl && \ + make -C sqlite3 -f ../mf-sqlite3.mingw-cross sqlite3.c +if test -r sqlite3/sqlite3.c -a -f "$WITH_SEE" ; then + cat sqlite3/sqlite3.c "$WITH_SEE" >sqlite3.c + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_HAS_CODEC=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_ACTIVATION_KEY=\\\"$SEE_KEY\\\"" + ADD_CFLAGS="$ADD_CFLAGS -DSEEEXT=\\\"$SEEEXT\\\"" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_API=static -DWIN32=1 -DNDEBUG=1 -DNO_TCL" + ADD_CFLAGS="$ADD_CFLAGS -DTHREADSAFE=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_DLL=1 -DSQLITE_TRHEADSAFE=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_OS_WIN=1 -DSQLITE_ASCII=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_SOUNDEX=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_ENABLE_COLUMN_METADATA=1" + ADD_CFLAGS="$ADD_CFLAGS -DWITHOUT_SHELL=1" + export ADD_CFLAGS + ADD_NSIS="$ADD_NSIS -DWITHOUT_SQLITE3_EXE" + unset SQLITE3_A10N_O + unset SQLITE3_EXE +fi +if test -n "$SQLITE_DLLS" ; then + make -C sqlite3 -f ../mf-sqlite3.mingw-cross sqlite3.dll +fi + +echo "===================" +echo "Building TinyCC ..." +echo "===================" +( $notcc && echo '*** skipped (NO_TCCEXT)' ) || true +$notcc || ( cd tcc ; sh mingw-cross-build.sh ) +# copy SQLite headers into TCC install include directory +$notcc || $nov2 || cp -p sqlite/sqlite.h TCC/include +$notcc || cp -p sqlite3/sqlite3.h sqlite3/src/sqlite3ext.h TCC/include +# copy LGPL to TCC install doc directory +$notcc || cp -p tcc-${TCCVER}/COPYING TCC/doc +$notcc || cp -p tcc-${TCCVER}/README TCC/doc/readme.txt + +echo "===============================" +echo "Building ODBC drivers and utils" +echo "===============================" +if $nov2 ; then + make -f Makefile.mingw-cross all_no2 +else + make -f Makefile.mingw-cross +fi +make -f Makefile.mingw-cross sqlite3odbc${SEEEXT}nw.dll + +echo "==========================" +echo "Building SQLite 2 ... UTF8" +echo "==========================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -C sqlite -f ../mf-sqlite.mingw-cross clean +$nov2 || make -C sqlite -f ../mf-sqlite.mingw-cross ENCODING=UTF8 all +if test -n "$SQLITE_DLLS" ; then + $nov2 || \ + make -C sqlite -f ../mf-sqlite.mingw-cross ENCODING=UTF8 sqliteu.dll +fi + +echo "=========================" +echo "Building drivers ... UTF8" +echo "=========================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -f Makefile.mingw-cross sqliteodbcu.dll sqliteu.exe + +echo "===================================" +echo "Building SQLite3 FTS extensions ..." +echo "===================================" +make -C sqlite3 -f ../mf-sqlite3fts.mingw-cross clean all +mv sqlite3/sqlite3_mod_fts*.dll . + +echo "=====================================" +echo "Building SQLite3 rtree extensions ..." +echo "=====================================" +make -C sqlite3 -f ../mf-sqlite3rtree.mingw-cross clean all +mv sqlite3/sqlite3_mod_rtree.dll . + +echo "========================================" +echo "Building SQLite3 extension functions ..." +echo "========================================" +make -f mf-sqlite3extfunc.mingw-cross clean all + +echo "============================" +echo "Building DLL import defs ..." +echo "============================" +# requires wine: create .def files with tiny_impdef.exe +# for all .dll files which provide SQLite +( $notcc && echo '*** skipped (NO_TCCEXT)' ) || true +$notcc || $nov2 || wine TCC/tiny_impdef.exe \ + sqliteodbc.dll -o TCC/lib/sqlite.def +$notcc || $nov2 || wine TCC/tiny_impdef.exe \ + sqliteodbcu.dll -o TCC/lib/sqliteu.def +$notcc || wine TCC/tiny_impdef.exe sqlite3odbc.dll -o TCC/lib/sqlite3.def + +if test -n "$SQLITE_DLLS" ; then + $nov2 || mv sqlite/sqlite.dll . + $nov2 || mv sqlite/sqliteu.dll . + mv sqlite3/sqlite3.dll . +fi + +if test -n "$SQLITE_DLLS" ; then + $notcc || $nov2 || wine TCC/tiny_impdef.exe \ + sqlite.dll -o TCC/lib/sqlite.def + $notcc || $nov2 || wine TCC/tiny_impdef.exe \ + sqliteu.dll -o TCC/lib/sqliteu.def + $notcc || wine TCC/tiny_impdef.exe sqlite3.dll -o TCC/lib/sqlite3.def +fi + +echo "=======================" +echo "Cleanup after build ..." +echo "=======================" +$nov2 || make -C sqlite -f ../mf-sqlite.mingw-cross clean +$nov2 || rm -f sqlite/sqlite.exe +mv sqlite3/sqlite3.c sqlite3/sqlite3.amalg +make -C sqlite3 -f ../mf-sqlite3.mingw-cross clean +rm -f sqlite3/sqlite3.exe +make -C sqlite3 -f ../mf-sqlite3fts.mingw-cross clean +make -C sqlite3 -f ../mf-sqlite3rtree.mingw-cross clean +mv sqlite3/sqlite3.amalg sqlite3/sqlite3.c +make -f mf-sqlite3extfunc.mingw-cross semiclean + +echo "===========================" +echo "Creating NSIS installer ..." +echo "===========================" +cp -p README readme.txt +unix2dos < license.terms > license.txt || todos < license.terms > license.txt +$notcc || unix2dos -k TCC/doc/COPYING || unix2dos -p TCC/doc/COPYING || \ + todos -p TCC/doc/COPYING +$notcc || unix2dos -k TCC/doc/readme.txt || unix2dos -p TCC/doc/readme.txt || \ + todos -p TCC/doc/readme.txt +makensis $ADD_NSIS sqliteodbc.nsi + diff --git a/lang/sql/odbc/mingw64-cross-build.sh b/lang/sql/odbc/mingw64-cross-build.sh new file mode 100644 index 00000000..ffff00ad --- /dev/null +++ b/lang/sql/odbc/mingw64-cross-build.sh @@ -0,0 +1,1705 @@ +#!/bin/sh +# +# Build script for cross compiling and packaging SQLite 3 +# ODBC drivers and tools for Win32 using MinGW and NSIS. +# Tested on Fedora Core 3/5/8, Debian Etch, RHEL 5/6. +# +# Cross toolchain and NSIS for Linux/i386/x86_64 can be fetched from +# http://www.ch-werner.de/xtools/crossmingw64-0.3-1.i386.rpm +# http://www.ch-werner.de/xtools/crossmingw64-0.3-1.x86_64.rpm +# http://www.ch-werner.de/xtools/nsis-2.37-1.i386.rpm +# or +# http://www.ch-werner.de/xtools/crossmingw64-0.3.i386.tar.bz2 +# http://www.ch-werner.de/xtools/crossmingw64-0.3.x86_64.tar.bz2 +# http://www.ch-werner.de/xtools/nsis-2.37-1_i386.tar.gz + +# Some aspects of the build process can be controlled by shell variables: +# +# NO_SQLITE2=1 omit building SQLite 2 and drivers for it +# SQLITE_DLLS=1 build and package driver with sqlite3.dll +# SQLITE_DLLS=2 build driver with refs to sqlite3.dll +# driver can use System.Data.SQLite.dll instead + +set -e + +VER2=2.8.17 +VER3=3.8.2 +VER3X=3080200 +VERZ=1.2.7 + +nov2=false +if test -n "$NO_SQLITE2" ; then + nov2=true + ADD_NSIS="-DWITHOUT_SQLITE2" +fi + +if test -f "$WITH_SEE" ; then + export SEEEXT=see + ADD_NSIS="-DWITH_SEE=$SEEEXT" + if test "$SQLITE_DLLS" = "2" ; then + SQLITE_DLLS=1 + fi +fi + +if test "$SQLITE_DLLS" = "2" ; then + # turn on -DSQLITE_DYNLOAD in sqlite3odbc.c + export ADD_CFLAGS="-DWITHOUT_SHELL=1 -DWITH_SQLITE_DLLS=2" + ADD_NSIS="$ADD_NSIS -DWITHOUT_SQLITE3_EXE" +elif test -n "$SQLITE_DLLS" ; then + export ADD_CFLAGS="-DWITHOUT_SHELL=1 -DWITH_SQLITE_DLLS=1" + export SQLITE3_DLL="-Lsqlite3 -lsqlite3" + export SQLITE3_EXE="sqlite3.exe" + ADD_NSIS="$ADD_NSIS -DWITH_SQLITE_DLLS" +else + export SQLITE3_A10N_O="sqlite3a10n.o" + export SQLITE3_EXE="sqlite3.exe" +fi + +if test -n "$WITH_SOURCES" ; then + ADD_NSIS="$ADD_NSIS -DWITH_SOURCES" +fi + +echo "==================" +echo "Preparing zlib ..." +echo "==================" +test -r zlib-${VERZ}.tar.gz || \ + wget -c http://zlib.net/zlib-${VERZ}.tar.gz || exit 1 +rm -rf zlib-${VERZ} +tar xzf zlib-${VERZ}.tar.gz +ln -sf zlib-${VERZ} zlib + +echo "====================" +echo "Preparing sqlite ..." +echo "====================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || test -r sqlite-${VER2}.tar.gz || \ + wget -c https://www.sqlite.org/sqlite-${VER2}.tar.gz \ + --no-check-certificate +$nov2 || test -r sqlite-${VER2}.tar.gz || exit 1 + +$nov2 || rm -f sqlite +$nov2 || tar xzf sqlite-${VER2}.tar.gz +$nov2 || ln -sf sqlite-${VER2} sqlite + +# enable sqlite_encode_binary et.al. +$nov2 || patch sqlite/main.mk <<'EOD' +--- sqlite.orig/main.mk 2005-04-24 00:43:23.000000000 +0200 ++++ sqlite/main.mk 2006-03-16 14:29:55.000000000 +0100 +@@ -55,7 +55,7 @@ + # Object files for the SQLite library. + # + LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ +- expr.o func.o hash.o insert.o \ ++ expr.o func.o hash.o insert.o encode.o \ + main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \ + select.o table.o tokenize.o trigger.o update.o util.o \ + vacuum.o vdbe.o vdbeaux.o where.o tclsqlite.o +EOD + +# display encoding +$nov2 || patch sqlite/src/shell.c <<'EOD' +--- sqlite.orig/src/shell.c 2005-04-24 00:43:22.000000000 +0200 ++++ sqlite/src/shell.c 2006-05-23 08:22:01.000000000 +0200 +@@ -1180,6 +1180,7 @@ + " -separator 'x' set output field separator (|)\n" + " -nullvalue 'text' set text string for NULL values\n" + " -version show SQLite version\n" ++ " -encoding show SQLite encoding\n" + " -help show this text, also show dot-commands\n" + ; + static void usage(int showDetail){ +@@ -1297,7 +1298,10 @@ + }else if( strcmp(z,"-echo")==0 ){ + data.echoOn = 1; + }else if( strcmp(z,"-version")==0 ){ +- printf("%s\n", sqlite_version); ++ printf("%s\n", sqlite_libversion()); ++ return 1; ++ }else if( strcmp(z,"-encoding")==0 ){ ++ printf("%s\n", sqlite_libencoding()); + return 1; + }else if( strcmp(z,"-help")==0 ){ + usage(1); +@@ -1330,9 +1334,9 @@ + char *zHome; + char *zHistory = 0; + printf( +- "SQLite version %s\n" ++ "SQLite version %s encoding %s\n" + "Enter \".help\" for instructions\n", +- sqlite_version ++ sqlite_libversion(), sqlite_libencoding() + ); + zHome = find_home_dir(); + if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){ +EOD + +# use open file dialog when no database name given +# need to link with -lcomdlg32 when enabled +true || patch sqlite/src/shell.c <<'EOD' +--- sqlite.orig/src/shell.c 2006-07-23 11:18:13.000000000 +0200 ++++ sqlite/src/shell.c 2006-07-23 11:30:26.000000000 +0200 +@@ -20,6 +20,10 @@ + #include "sqlite.h" + #include <ctype.h> + ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) + # include <signal.h> + # include <pwd.h> +@@ -1246,6 +1250,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + data.zDbFilename = ":memory:"; + } + if( i<argc ){ +EOD + +# same but new module libshell.c +$nov2 || patch sqlite/main.mk <<'EOD' +--- sqlite.orig/main.mk 2007-01-10 19:30:52.000000000 +0100 ++++ sqlite/main.mk 2007-01-10 19:33:39.000000000 +0100 +@@ -54,7 +54,7 @@ + + # Object files for the SQLite library. + # +-LIBOBJ = attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ ++LIBOBJ += attach.o auth.o btree.o btree_rb.o build.o copy.o date.o delete.o \ + expr.o func.o hash.o insert.o encode.o \ + main.o opcodes.o os.o pager.o parse.o pragma.o printf.o random.o \ + select.o table.o tokenize.o trigger.o update.o util.o \ +EOD +$nov2 || cp -p sqlite/src/shell.c sqlite/src/libshell.c +$nov2 || patch sqlite/src/libshell.c <<'EOD' +--- sqlite.orig/src/libshell.c 2007-01-10 19:13:01.000000000 +0100 ++++ sqlite/src/libshell.c 2007-01-10 19:25:56.000000000 +0100 +@@ -20,6 +20,10 @@ + #include "sqlite.h" + #include <ctype.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) + # include <signal.h> + # include <pwd.h> +@@ -1205,7 +1209,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1246,6 +1250,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + data.zDbFilename = ":memory:"; + } + if( i<argc ){ +EOD + +$nov2 || rm -f sqlite/src/minshell.c +$nov2 || touch sqlite/src/minshell.c +$nov2 || patch sqlite/src/minshell.c <<'EOD' +--- sqlite.orig/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 ++++ sqlite/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 +@@ -0,0 +1,20 @@ ++/* ++** 2001 September 15 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++** ++************************************************************************* ++** This file contains code to implement the "sqlite" command line ++** utility for accessing SQLite databases. ++*/ ++ ++int sqlite_main(int argc, char **argv); ++ ++int main(int argc, char **argv){ ++ return sqlite_main(argc, argv); ++} +EOD + +echo "=====================" +echo "Preparing sqlite3 ..." +echo "=====================" +test -r sqlite-src-${VER3X}.zip || \ + wget -c https://www.sqlite.org/2013/sqlite-src-${VER3X}.zip \ + --no-check-certificate +test -r sqlite-src-${VER3X}.zip || exit 1 +test -r extension-functions.c || + wget -O extension-functions.c -c \ + 'https://www.sqlite.org/contrib/download/extension-functions.c?get=25' \ + --no-check-certificate +if test -r extension-functions.c ; then + cp extension-functions.c extfunc.c + patch < extfunc.patch +fi +test -r extfunc.c || exit 1 + +rm -f sqlite3 +rm -rf sqlite-src-${VER3X} +unzip sqlite-src-${VER3X}.zip +ln -sf sqlite-src-${VER3X} sqlite3 + +patch sqlite3/main.mk <<'EOD' +--- sqlite3.orig/main.mk 2007-03-31 14:32:21.000000000 +0200 ++++ sqlite3/main.mk 2007-04-02 11:04:50.000000000 +0200 +@@ -67,7 +67,7 @@ + + # All of the source code files. + # +-SRC = \ ++SRC += \ + $(TOP)/src/alter.c \ + $(TOP)/src/analyze.c \ + $(TOP)/src/attach.c \ +EOD + +# use open file dialog when no database name given +# need to link with -lcomdlg32 when enabled +true || patch sqlite3/src/shell.c <<'EOD' +--- sqlite3.orig/src/shell.c 2006-06-06 14:32:21.000000000 +0200 ++++ sqlite3/src/shell.c 2006-07-23 11:04:50.000000000 +0200 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) + # include <signal.h> + # include <pwd.h> +@@ -1676,6 +1676,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && defined(DRIVER_VER_INFO) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD +# SQLite 3.5.1 Win32 mutex fix +test "$VER3" != "3.5.1" || patch sqlite3/src/mutex_w32.c <<'EOD' +--- sqlite3.orig/src/mutex_w32.c 2007-08-30 14:10:30 ++++ sqlite3/src/mutex_w32.c 2007-09-04 22:31:3 +@@ -141,6 +141,12 @@ + p->nRef++; + } + int sqlite3_mutex_try(sqlite3_mutex *p){ ++ /* The TryEnterCriticalSection() interface is not available on all ++ ** windows systems. Since sqlite3_mutex_try() is only used as an ++ ** optimization, we can skip it on windows. */ ++ return SQLITE_BUSY; ++ ++#if 0 /* Not Available */ + int rc; + assert( p ); + assert( p->id==SQLITE_MUTEX_RECURSIVE || sqlite3_mutex_notheld(p) ); +@@ -152,6 +158,7 @@ + rc = SQLITE_BUSY; + } + return rc; ++#endif + } + + /* + +EOD + +# same but new module libshell.c +cp -p sqlite3/src/shell.c sqlite3/src/libshell.c +test "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2007-01-08 23:40:05.000000000 +0100 ++++ sqlite3/src/libshell.c 2007-01-10 18:35:43.000000000 +0100 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) + # include <signal.h> + # include <pwd.h> +@@ -1774,7 +1778,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1816,6 +1820,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +test "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2007-01-08 23:40:05.000000000 +0100 ++++ sqlite3/src/libshell.c 2007-01-10 18:35:43.000000000 +0100 +@@ -21,6 +21,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++# include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) + # include <signal.h> + # include <pwd.h> +@@ -1774,7 +1778,7 @@ + strcpy(continuePrompt," ...> "); + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -1816,6 +1820,17 @@ + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +test "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + -o "$VER3" = "3.7.16" -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" \ + -o "$VER3" = "3.7.17" -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" \ + -o "$VER3" = "3.8.2" \ + && patch sqlite3/src/libshell.c <<'EOD' +--- sqlite3.orig/src/libshell.c 2012-12-12 14:42:10.000000000 +0100 ++++ sqlite3/src/libshell.c 2012-12-13 12:14:57.000000000 +0100 +@@ -36,6 +36,10 @@ + #include <ctype.h> + #include <stdarg.h> + ++#ifdef _WIN32 ++#include <windows.h> ++#endif ++ + #if !defined(_WIN32) && !defined(WIN32) + # include <signal.h> + # if !defined(__RTP__) && !defined(_WRS_KERNEL) +@@ -2894,7 +2898,7 @@ + return argv[i]; + } + +-int main(int argc, char **argv){ ++int sqlite3_main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; +@@ -2996,6 +3000,17 @@ + } + } + if( data.zDbFilename==0 ){ ++#if defined(_WIN32) && !defined(__TINYC__) ++ static OPENFILENAME ofn; ++ static char zDbFn[1024]; ++ ofn.lStructSize = sizeof(ofn); ++ ofn.lpstrFile = (LPTSTR) zDbFn; ++ ofn.nMaxFile = sizeof(zDbFn); ++ ofn.Flags = OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR; ++ if( GetOpenFileName(&ofn) ){ ++ data.zDbFilename = zDbFn; ++ } else ++#endif + #ifndef SQLITE_OMIT_MEMORYDB + data.zDbFilename = ":memory:"; + #else +EOD + +rm -f sqlite3/src/minshell.c +touch sqlite3/src/minshell.c +patch sqlite3/src/minshell.c <<'EOD' +--- sqlite3.orig/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 ++++ sqlite3/src/minshell.c 2007-01-10 18:46:47.000000000 +0100 +@@ -0,0 +1,20 @@ ++/* ++** 2001 September 15 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++** ++************************************************************************* ++** This file contains code to implement the "sqlite" command line ++** utility for accessing SQLite databases. ++*/ ++ ++int sqlite3_main(int argc, char **argv); ++ ++int main(int argc, char **argv){ ++ return sqlite3_main(argc, argv); ++} +EOD + +# amalgamation: add libshell.c +test "$VER3" != "3.5.6" && test -r sqlite3/tool/mksqlite3c.tcl && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/tool/mksqlite3c.tcl 2007-04-02 14:20:10.000000000 +0200 ++++ sqlite3/tool/mksqlite3c.tcl 2007-04-03 09:42:03.000000000 +0200 +@@ -194,6 +194,7 @@ + where.c + + parse.c ++ libshell.c + + tokenize.c + complete.c +EOD +test "$VER3" = "3.5.6" && test -r sqlite3/tool/mksqlite3c.tcl && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/tool/mksqlite3c.tcl 2007-04-02 14:20:10.000000000 +0200 ++++ sqlite3/tool/mksqlite3c.tcl 2007-04-03 09:42:03.000000000 +0200 +@@ -200,6 +200,7 @@ + + main.c + ++ libshell.c + fts3.c + fts3_hash.c + fts3_porter.c +EOD + +# patch: parse foreign key constraints on virtual tables +test "$VER3" != "3.6.15" -a "$VER3" != "3.6.16" -a "$VER3" != "3.6.17" \ + -a "$VER3" != "3.6.18" -a "$VER3" != "3.6.19" -a "$VER3" != "3.6.20" \ + -a "$VER3" != "3.6.21" -a "$VER3" != "3.6.22" -a "$VER3" != "3.6.23" \ + -a "$VER3" != "3.6.23.1" -a "$VER3" != "3.7.0" -a "$VER3" != "3.7.0.1" \ + -a "$VER3" != "3.7.1" -a "$VER3" != "3.7.2" -a "$VER3" != "3.7.3" \ + -a "$VER3" != "3.7.4" -a "$VER3" != "3.7.5" -a "$VER3" != "3.7.6" \ + -a "$VER3" != "3.7.6.1" -a "$VER3" != "3.7.6.2" -a "$VER3" != "3.7.6.3" \ + -a "$VER3" != "3.7.7" -a "$VER3" != "3.7.7.1" -a "$VER3" != "3.7.8" \ + -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" -a "$VER3" != "3.7.11" \ + -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" -a "$VER3" != "3.7.13" \ + -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +diff -u sqlite3.orig/src/build.c sqlite3/src/build.c +--- sqlite3.orig/src/build.c 2007-01-09 14:53:04.000000000 +0100 ++++ sqlite3/src/build.c 2007-01-30 08:14:41.000000000 +0100 +@@ -2063,7 +2063,7 @@ + char *z; + + assert( pTo!=0 ); +- if( p==0 || pParse->nErr || IN_DECLARE_VTAB ) goto fk_end; ++ if( p==0 || pParse->nErr ) goto fk_end; + if( pFromCol==0 ){ + int iCol = p->nCol-1; + if( iCol<0 ) goto fk_end; +diff -u sqlite3.orig/src/pragma.c sqlite3/src/pragma.c +--- sqlite3.orig/src/pragma.c 2007-01-27 03:24:56.000000000 +0100 ++++ sqlite3/src/pragma.c 2007-01-30 09:19:30.000000000 +0100 +@@ -589,6 +589,9 @@ + pTab = sqlite3FindTable(db, zRight, zDb); + if( pTab ){ + v = sqlite3GetVdbe(pParse); ++#ifndef SQLITE_OMIT_VIRTUAL_TABLE ++ if( pTab->pVtab ) sqlite3ViewGetColumnNames(pParse, pTab); ++#endif + pFK = pTab->pFKey; + if( pFK ){ + int i = 0; +diff -u sqlite3.orig/src/vtab.c sqlite3/src/vtab.c +--- sqlite3.orig/src/vtab.c 2007-01-09 15:01:14.000000000 +0100 ++++ sqlite3/src/vtab.c 2007-01-30 08:23:22.000000000 +0100 +@@ -540,6 +540,9 @@ + int rc = SQLITE_OK; + Table *pTab = db->pVTab; + char *zErr = 0; ++#ifndef SQLITE_OMIT_FOREIGN_KEYS ++ FKey *pFKey; ++#endif + + sqlite3_mutex_enter(db->mutex); + pTab = db->pVTab; +@@ -568,6 +571,15 @@ + } + sParse.declareVtab = 0; + ++#ifndef SQLITE_OMIT_FOREIGN_KEYS ++ assert( pTab->pFKey==0 ); ++ pTab->pFKey = sParse.pNewTable->pFKey; ++ sParse.pNewTable->pFKey = 0; ++ for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ ++ pFKey->pFrom=pTab; ++ } ++#endif ++ + if( sParse.pVdbe ){ + sqlite3VdbeFinalize(sParse.pVdbe); + } +EOD + +# patch: re-enable NO_TCL in tclsqlite.c (3.3.15) +patch -d sqlite3 -p1 <<'EOD' +diff -u sqlite3.orig/src/tclsqlite.c sqlite3/src/tclsqlite.c +--- sqlite3.orig/src/tclsqlite.c 2007-04-06 17:02:14.000000000 +0200 ++++ sqlite3/src/tclsqlite.c 2007-04-10 07:47:49.000000000 +0200 +@@ -14,6 +14,7 @@ + ** + ** $Id: mingw64-cross-build.sh,v 1.46 2013/12/08 10:37:28 chw Exp chw $ + */ ++#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ + #include "tcl.h" + + /* +@@ -2264,3 +2265,5 @@ + return 0; + } + #endif /* TCLSH */ ++ ++#endif /* !defined(NO_TCL) */ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 < 3.4.0 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2007-04-11 19:52:04.000000000 +0200 ++++ sqlite3/src/os_win.c 2007-05-08 06:57:06.000000000 +0200 +@@ -1237,8 +1237,8 @@ + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; +- if( pFile->locktype==NO_LOCK +- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) ++ if( locktype==SHARED_LOCK ++ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -1289,6 +1289,18 @@ + newLocktype = EXCLUSIVE_LOCK; + }else{ + OSTRACE2("error-code = %d\n", GetLastError()); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -1362,6 +1374,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 >= 3.5.4 && <= 3.6.10 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2007-12-13 22:38:58.000000000 +0100 ++++ sqlite3/src/os_win.c 2008-01-18 10:01:48.000000000 +0100 +@@ -855,8 +855,8 @@ + ** the PENDING_LOCK byte is temporary. + */ + newLocktype = pFile->locktype; +- if( pFile->locktype==NO_LOCK +- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) ++ if( locktype==SHARED_LOCK ++ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -907,7 +907,18 @@ + newLocktype = EXCLUSIVE_LOCK; + }else{ + OSTRACE2("error-code = %d\n", GetLastError()); +- getReadLock(pFile); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -982,6 +993,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: Win32 locking and pager unlock, for SQLite3 >= 3.6.11 && < 3.7.0 +true || patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/os_win.c 2009-02-15 14:07:09.000000000 +0100 ++++ sqlite3/src/os_win.c 2009-02-20 16:39:48.000000000 +0100 +@@ -922,7 +922,7 @@ + newLocktype = pFile->locktype; + if( (pFile->locktype==NO_LOCK) + || ( (locktype==EXCLUSIVE_LOCK) +- && (pFile->locktype==RESERVED_LOCK)) ++ && (pFile->locktype<RESERVED_LOCK)) + ){ + int cnt = 3; + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +@@ -981,7 +981,18 @@ + }else{ + error = GetLastError(); + OSTRACE2("error-code = %d\n", error); +- getReadLock(pFile); ++ if( !getReadLock(pFile) ){ ++ /* This should never happen. We should always be able to ++ ** reacquire the read lock */ ++ OSTRACE1("could not re-get a SHARED lock.\n"); ++ if( newLocktype==PENDING_LOCK || pFile->locktype==PENDING_LOCK ){ ++ UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); ++ } ++ if( pFile->locktype==RESERVED_LOCK ){ ++ UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); ++ } ++ newLocktype = NO_LOCK; ++ } + } + } + +@@ -1057,6 +1068,7 @@ + /* This should never happen. We should always be able to + ** reacquire the read lock */ + rc = SQLITE_IOERR_UNLOCK; ++ locktype = NO_LOCK; + } + } + if( type>=RESERVED_LOCK ){ +EOD + +# patch: compile fix for FTS3 as extension module +test "$VER3" != "3.6.21" -a "$VER3" != "3.6.22" -a "$VER3" != "3.6.23" \ + -a "$VER3" != "3.6.23.1" -a "$VER3" != "3.7.0" -a "$VER3" != "3.7.0.1" \ + -a "$VER3" != "3.7.1" -a "$VER3" != "3.7.2" -a "$VER3" != "3.7.3" \ + -a "$VER3" != "3.7.4" -a "$VER3" != "3.7.5" -a "$VER3" != "3.7.6" \ + -a "$VER3" != "3.7.6.1" -a "$VER3" != "3.7.6.2" -a "$VER3" != "3.7.6.3" \ + -a "$VER3" != "3.7.7" -a "$VER3" != "3.7.7.1" -a "$VER3" != "3.7.8" \ + -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" -a "$VER3" != "3.7.11" \ + -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" -a "$VER3" != "3.7.13" \ + -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" -a "$VER3" != "3.7.15" \ + -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" -a "$VER3" != "3.7.16" \ + -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" -a "$VER3" != "3.7.17" \ + -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2008-02-02 17:24:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2008-03-16 11:29:02.000000000 +0100 +@@ -274,10 +274,6 @@ + + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stdio.h> +@@ -6389,7 +6385,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +--- sqlite3.orig/ext/fts3/fts3_porter.c 2008-02-01 16:40:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_porter.c 2008-03-16 11:34:50.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + /* +--- sqlite3.orig/ext/fts3/fts3_tokenizer1.c 2007-11-23 18:31:18.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer1.c 2008-03-16 11:35:37.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + typedef struct simple_tokenizer { +--- sqlite3.orig/ext/fts3/fts3_hash.c 2007-11-24 01:41:52.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_hash.c 2008-03-16 11:39:57.000000000 +0100 +@@ -29,6 +29,11 @@ + #include <stdlib.h> + #include <string.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "sqlite3.h" + #include "fts3_hash.h" + +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2008-06-24 03:29:58.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2008-07-17 08:38:24.000000000 +0200 +@@ -27,7 +27,7 @@ + + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + + #include "fts3_hash.h" +EOD +test "$VER3" = "3.6.21" -o "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" \ + -o "$VER3" = "3.6.23.1" -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" -o "$VER3" = "3.7.5" \ + -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2008-02-02 17:24:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2008-03-16 11:29:02.000000000 +0100 +@@ -274,10 +274,6 @@ + + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include "fts3Int.h" + + #include <assert.h> +@@ -6389,7 +6385,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD +patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_porter.c 2008-02-01 16:40:34.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_porter.c 2008-03-16 11:34:50.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + /* +--- sqlite3.orig/ext/fts3/fts3_tokenizer1.c 2007-11-23 18:31:18.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer1.c 2008-03-16 11:35:37.000000000 +0100 +@@ -31,6 +31,11 @@ + #include <string.h> + #include <ctype.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "fts3_tokenizer.h" + + typedef struct simple_tokenizer { +EOD +test "$VER3" != "3.7.8" -a "$VER3" != "3.7.9" -a "$VER3" != "3.7.10" \ + -a "$VER3" != "3.7.11" -a "$VER3" != "3.7.12" -a "$VER3" != "3.7.12.1" \ + -a "$VER3" != "3.7.13" -a "$VER3" != "3.7.14" -a "$VER3" != "3.7.14.1" \ + -a "$VER3" != "3.7.15" -a "$VER3" != "3.7.15.1" -a "$VER3" != "3.7.15.2" \ + -a "$VER3" != "3.7.16" -a "$VER3" != "3.7.16.1" -a "$VER3" != "3.7.16.2" \ + -a "$VER3" != "3.7.17" -a "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" \ + -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_hash.c 2007-11-24 01:41:52.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_hash.c 2008-03-16 11:39:57.000000000 +0100 +@@ -29,6 +29,11 @@ + #include <stdlib.h> + #include <string.h> + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "sqlite3.h" + #include "fts3_hash.h" + +EOD +test "$VER3" = "3.6.21" && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2009-12-03 20:39:06.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-01-05 07:59:27.000000000 +0100 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +EOD +test "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" -o "$VER3" = "3.6.23.1" \ + -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2010-01-05 09:42:19.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-01-05 09:55:25.000000000 +0100 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +@@ -2226,7 +2230,7 @@ + + if( !zVal ){ + return SQLITE_NOMEM; +- }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ ++ }else if( nVal==8 && 0==strnicmp(zVal, "optimize", 8) ){ + rc = fts3SegmentMerge(p, -1); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; +@@ -2234,10 +2238,10 @@ + sqlite3Fts3PendingTermsClear(p); + } + #ifdef SQLITE_TEST +- }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ ++ }else if( nVal>9 && 0==strnicmp(zVal, "nodesize=", 9) ){ + p->nNodeSize = atoi(&zVal[9]); + rc = SQLITE_OK; +- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ ++ }else if( nVal>11 && 0==strnicmp(zVal, "maxpending=", 9) ){ + p->nMaxPendingData = atoi(&zVal[11]); + rc = SQLITE_OK; + #endif +EOD + +test "$VER3" = "3.7.6"\ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_write.c 2011-04-12 11:44:56.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_write.c 2011-04-13 08:00:51.000000000 +0200 +@@ -20,6 +20,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +@@ -2450,7 +2454,7 @@ + + if( !zVal ){ + return SQLITE_NOMEM; +- }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ ++ }else if( nVal==8 && 0==strnicmp(zVal, "optimize", 8) ){ + rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; +@@ -2458,10 +2462,10 @@ + sqlite3Fts3PendingTermsClear(p); + } + #ifdef SQLITE_TEST +- }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ ++ }else if( nVal>9 && 0==strnicmp(zVal, "nodesize=", 9) ){ + p->nNodeSize = atoi(&zVal[9]); + rc = SQLITE_OK; +- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ ++ }else if( nVal>11 && 0==strnicmp(zVal, "maxpending=", 9) ){ + p->nMaxPendingData = atoi(&zVal[11]); + rc = SQLITE_OK; + #endif +--- sqlite3.orig/ext/fts3/fts3_aux.c 2011-04-12 11:44:56.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_aux.c 2011-04-13 08:16:17.000000000 +0200 +@@ -15,6 +15,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD + +test "$VER3" = "3.6.21" -o "$VER3" = "3.6.22" -o "$VER3" = "3.6.23" \ + -o "$VER3" = "3.6.23.1" -o "$VER3" = "3.7.0" -o "$VER3" = "3.7.0.1" \ + -o "$VER3" = "3.7.1" -o "$VER3" = "3.7.2" -o "$VER3" = "3.7.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_snippet.c 2010-01-05 08:03:51.000000000 +0100 +@@ -14,6 +14,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <ctype.h> +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2010-01-05 08:06:10.000000000 +0100 +@@ -17,6 +17,11 @@ + */ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +@@ -445,7 +450,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = (int)strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = (int)((zInput - z) + nStr + 1); +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2009-12-07 17:38:46.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2010-01-05 08:12:50.000000000 +0100 +@@ -27,7 +27,7 @@ + + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + + #include "fts3Int.h" +@@ -166,7 +166,7 @@ + if( !z ){ + zCopy = sqlite3_mprintf("simple"); + }else{ +- if( sqlite3_strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){ ++ if( strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){ + return SQLITE_OK; + } + zCopy = sqlite3_mprintf("%s", &z[8]); +EOD + +test "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_snippet.c 2010-01-05 08:03:51.000000000 +0100 +@@ -14,6 +14,10 @@ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + #include "fts3Int.h" ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <ctype.h> +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2009-12-07 17:38:46.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2010-01-05 08:12:50.000000000 +0100 +@@ -27,7 +27,7 @@ + + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + + #include "fts3Int.h" +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-12-03 12:33:32.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2010-01-05 08:06:10.000000000 +0100 +@@ -17,6 +17,11 @@ + */ + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +@@ -445,7 +450,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = (int)strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = (int)((zInput - z) + nStr + 1); +--- sqlite3.orig/ext/fts3/fts3.c 2010-12-07 16:14:36.000000000 +0100 ++++ sqlite3/ext/fts3/fts3.c 2010-12-16 11:59:02.000000000 +0100 +@@ -702,8 +698,8 @@ + sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ + + assert( strlen(argv[0])==4 ); +- assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) +- || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) ++ assert( (strnicmp(argv[0], "fts4", 4)==0 && isFts4) ++ || (strnicmp(argv[0], "fts3", 4)==0 && !isFts4) + ); + + nDb = (int)strlen(argv[1]) + 1; +@@ -732,7 +728,7 @@ + /* Check if this is a tokenizer specification */ + if( !pTokenizer + && strlen(z)>8 +- && 0==sqlite3_strnicmp(z, "tokenize", 8) ++ && 0==strnicmp(z, "tokenize", 8) + && 0==sqlite3Fts3IsIdChar(z[8]) + ){ + rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr); +@@ -744,8 +740,8 @@ + rc = SQLITE_NOMEM; + goto fts3_init_out; + } +- if( nKey==9 && 0==sqlite3_strnicmp(z, "matchinfo", 9) ){ +- if( strlen(zVal)==4 && 0==sqlite3_strnicmp(zVal, "fts3", 4) ){ ++ if( nKey==9 && 0==strnicmp(z, "matchinfo", 9) ){ ++ if( strlen(zVal)==4 && 0==strnicmp(zVal, "fts3", 4) ){ + bNoDocsize = 1; + }else{ + *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); +--- sqlite3.orig/ext/fts3/fts3_write.c 2010-12-16 12:08:45.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_write.c 2010-12-16 12:48:30.000000000 +0100 +@@ -868,16 +868,16 @@ + assert( pnBlob); + + if( p->pSegments ){ +- rc = sqlite3_blob_reopen(p->pSegments, iBlockid); +- }else{ +- if( 0==p->zSegmentsTbl ){ +- p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); +- if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; +- } +- rc = sqlite3_blob_open( +- p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments +- ); ++ sqlite3_blob_close(p->pSegments); ++ p->pSegments = 0; + } ++ if( 0==p->zSegmentsTbl ){ ++ p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); ++ if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; ++ } ++ rc = sqlite3_blob_open( ++ p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments ++ ); + + if( rc==SQLITE_OK ){ + int nByte = sqlite3_blob_bytes(p->pSegments); +EOD + +# patch: FTS3 again, for SQLite3 >= 3.6.8 +test "$VER3" = "3.6.8" -o "$VER3" = "3.6.9" -o "$VER3" = "3.6.10" \ + -o "$VER3" = "3.6.11" -o "$VER3" = "3.6.12" -o "$VER3" = "3.6.13" \ + -o "$VER3" = "3.6.14" -o "$VER3" = "3.6.14.1" -o "$VER3" = "3.6.14.2" \ + -o "$VER3" = "3.6.15" -o "$VER3" = "3.6.16" -o "$VER3" = "3.6.17" \ + -o "$VER3" = "3.6.18" -o "$VER3" = "3.6.19" -o "$VER3" = "3.6.20" && \ + patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-01-01 15:06:13.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2009-01-14 09:55:13.000000000 +0100 +@@ -57,6 +57,12 @@ + #define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 + + #include "fts3_expr.h" ++ ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + #include "sqlite3.h" + #include <ctype.h> + #include <string.h> +EOD +test "$VER3" = "3.6.17" -o "$VER3" = "3.6.18" -o "$VER3" = "3.6.19" \ + -o "$VER3" = "3.6.20" && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2009-01-01 15:06:13.000000000 +0100 ++++ sqlite3/ext/fts3/fts3_expr.c 2009-01-14 09:55:13.000000000 +0100 +@@ -428,7 +428,7 @@ + const char *zStr = pParse->azCol[ii]; + int nStr = strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' +- && sqlite3_strnicmp(zStr, zInput, nStr)==0 ++ && memcmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = ((zInput - z) + nStr + 1); +EOD +# patch: compile fix for rtree as extension module +test "$VER3" != "3.8.0" -a "$VER3" != "3.8.1" -a "$VER3" != "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/rtree/rtree.c 2008-07-16 16:43:35.000000000 +0200 ++++ sqlite3/ext/rtree/rtree.c 2008-07-17 08:59:53.000000000 +0200 +@@ -2812,7 +2812,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD + +# patch: compile fix for rtree as extension module +test "$VER3" = "3.7.3" -o "$VER3" = "3.7.4" -o "$VER3" = "3.7.5" \ + -o "$VER3" = "3.7.6" \ + -o "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + -o "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/rtree/rtree.c 2010-10-16 10:53:54.000000000 +0200 ++++ sqlite3/ext/rtree/rtree.c 2010-10-16 11:12:32.000000000 +0200 +@@ -3193,6 +3193,8 @@ + return rc; + } + ++#ifdef SQLITE_CORE ++ + /* + ** A version of sqlite3_free() that can be used as a callback. This is used + ** in two places - as the destructor for the blob value returned by the +@@ -3257,6 +3259,8 @@ + ); + } + ++#endif ++ + #ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, +EOD + +# patch: .read shell command +test "$VER3" = "3.7.6.1" -o "$VER3" = "3.7.6.2" -o "$VER3" = "3.7.6.3" \ + -o "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/shell.c 2011-05-19 15:34:57.000000000 +0200 ++++ sqlite3/src/shell.c 2011-06-09 13:36:13.000000000 +0200 +@@ -1957,6 +1957,7 @@ + }else{ + rc = process_input(p, alt); + fclose(alt); ++ if( rc ) rc = 1; + } + }else + +EOD + +# patch: FTS3 for 3.7.7 plus missing APIs in sqlite3ext.h/loadext.c +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_aux.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_aux.c 2011-06-25 06:44:08.000000000 +0200 +@@ -14,6 +14,10 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3.c 2011-06-25 06:48:49.000000000 +0200 +@@ -295,10 +295,6 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stddef.h> +@@ -3136,7 +3132,7 @@ + return rc; + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, +EOD +test "$VER3" = "3.7.8" -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" \ + -o "$VER3" = "3.7.11" -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" \ + -o "$VER3" = "3.7.13" -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + -o "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + -o "$VER3" = "3.7.16" -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" \ + -o "$VER3" = "3.7.17" -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" \ + -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3.c 2011-09-19 20:46:52.000000000 +0200 ++++ sqlite3/ext/fts3/fts3.c 2011-09-20 09:47:40.000000000 +0200 +@@ -295,10 +295,6 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +-#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +-# define SQLITE_CORE 1 +-#endif +- + #include <assert.h> + #include <stdlib.h> + #include <stddef.h> +@@ -4826,7 +4822,7 @@ + } + } + +-#if !SQLITE_CORE ++#ifndef SQLITE_CORE + /* + ** Initialize API pointer table, if required. + */ +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" -o "$VER3" = "3.7.8" \ + -o "$VER3" = "3.7.9" -o "$VER3" = "3.7.10" -o "$VER3" = "3.7.11" \ + -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" -o "$VER3" = "3.7.13" \ + -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" -o "$VER3" = "3.7.15" \ + -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" -o "$VER3" = "3.7.16" \ + -o "$VER3" = "3.7.16.1" -o "$VER3" = "3.7.16.2" -o "$VER3" = "3.7.17" \ + -o "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER3" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_expr.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_expr.c 2011-06-25 06:47:00.000000000 +0200 +@@ -18,6 +18,11 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif ++ + /* + ** By default, this module parses the legacy syntax that has been + ** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +--- sqlite3.orig/ext/fts3/fts3_snippet.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_snippet.c 2011-06-25 06:45:47.000000000 +0200 +@@ -13,7 +13,10 @@ + + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) +- ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + +EOD +test "$VER3" = "3.7.7" -o "$VER3" = "3.7.7.1" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/ext/fts3/fts3_tokenizer.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_tokenizer.c 2011-06-25 06:50:19.000000000 +0200 +@@ -25,7 +25,7 @@ + */ + #include "sqlite3ext.h" + #ifndef SQLITE_CORE +- SQLITE_EXTENSION_INIT1 ++extern const sqlite3_api_routines *sqlite3_api; + #endif + #include "fts3Int.h" + +--- sqlite3.orig/ext/fts3/fts3_write.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/ext/fts3/fts3_write.c 2011-06-25 06:45:05.000000000 +0200 +@@ -20,6 +20,10 @@ + #include "fts3Int.h" + #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + ++#include "sqlite3ext.h" ++#ifndef SQLITE_CORE ++extern const sqlite3_api_routines *sqlite3_api; ++#endif + #include <string.h> + #include <assert.h> + #include <stdlib.h> +--- sqlite3.orig/src/sqlite3ext.h 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/src/sqlite3ext.h 2011-06-25 07:28:06.000000000 +0200 +@@ -212,6 +212,9 @@ + int (*wal_autocheckpoint)(sqlite3*,int); + int (*wal_checkpoint)(sqlite3*,const char*); + void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); ++ int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); ++ int (*vtab_config)(sqlite3*,int op,...); ++ int (*vtab_on_conflict)(sqlite3*); + }; + + /* +@@ -412,6 +415,9 @@ + #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint + #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint + #define sqlite3_wal_hook sqlite3_api->wal_hook ++#define sqlite3_blob_reopen sqlite3_api->blob_reopen ++#define sqlite3_vtab_config sqlite3_api->vtab_config ++#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict + #endif /* SQLITE_CORE */ + + #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; +--- sqlite3.orig/src/loadext.c 2011-06-24 09:06:08.000000000 +0200 ++++ sqlite3/src/loadext.c 2011-06-25 07:29:59.000000000 +0200 +@@ -84,6 +84,8 @@ + # define sqlite3_create_module 0 + # define sqlite3_create_module_v2 0 + # define sqlite3_declare_vtab 0 ++# define sqlite3_vtab_config 0 ++# define sqlite3_vtab_on_conflict 0 + #endif + + #ifdef SQLITE_OMIT_SHARED_CACHE +@@ -107,6 +109,7 @@ + #define sqlite3_blob_open 0 + #define sqlite3_blob_read 0 + #define sqlite3_blob_write 0 ++#define sqlite3_blob_reopen 0 + #endif + + /* +@@ -372,6 +375,18 @@ + 0, + 0, + #endif ++#ifndef SQLITE_OMIT_INCRBLOB ++ sqlite3_blob_reopen, ++#else ++ 0, ++#endif ++#ifndef SQLITE_OMIT_VIRTUALTABLE ++ sqlite3_vtab_config, ++ sqlite3_vtab_on_conflict, ++#else ++ 0, ++ 0, ++#endif + }; + + /* +EOD +test "$VER3" = "3.7.11" -o "$VER3" = "3.7.12" -o "$VER3" = "3.7.12.1" \ + -o "$VER3" = "3.7.13" -o "$VER3" = "3.7.14" -o "$VER3" = "3.7.14.1" \ + -o "$VER3" = "3.7.15" -o "$VER3" = "3.7.15.1" -o "$VER3" = "3.7.15.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/sqlite3ext.h 2012-03-22 20:13:33.000000000 +0100 ++++ sqlite3/src/sqlite3ext.h 2012-03-22 20:13:57.000000000 +0100 +@@ -236,6 +236,7 @@ + int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); + int (*vtab_config)(sqlite3*,int op,...); + int (*vtab_on_conflict)(sqlite3*); ++ int (*stricmp)(const char*,const char*); + }; + + /* +@@ -439,6 +440,7 @@ + #define sqlite3_blob_reopen sqlite3_api->blob_reopen + #define sqlite3_vtab_config sqlite3_api->vtab_config + #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict ++#define sqlite3_stricmp sqlite3_api->stricmp + #endif /* SQLITE_CORE */ + + #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; +--- sqlite3.orig/src/loadext.c 2012-03-20 15:20:13.000000000 +0100 ++++ sqlite3/src/loadext.c 2012-03-22 20:16:24.000000000 +0100 +@@ -378,6 +378,7 @@ + sqlite3_blob_reopen, + sqlite3_vtab_config, + sqlite3_vtab_on_conflict, ++ sqlite3_stricmp, + }; + + /* +EOD + +test "$VER3" = "3.8.0" -o "$VER3" = "3.8.1" -o "$VER" = "3.8.2" \ + && patch -d sqlite3 -p1 <<'EOD' +--- sqlite3.orig/src/loadext.c 2013-09-16 06:56:48.000000000 +0200 ++++ sqlite3/src/loadext.c 2013-09-16 06:58:14.000000000 +0200 +@@ -495,7 +495,8 @@ + memcpy(zAltEntry, "sqlite3_", 8); + for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} + iFile++; +- if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; ++ if( sqlite3_strnicmp(zFile+iFile, "sqlite3_mod_", 12)==0 ) iFile += 12; ++ else if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; + for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ + if( sqlite3Isalpha(c) ){ + zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; +EOD + +echo "========================" +echo "Cleanup before build ..." +echo "========================" +make -f Makefile.mingw64-cross clean +$notv2 || make -C sqlite -f ../mf-sqlite.mingw64-cross clean +make -C sqlite3 -f ../mf-sqlite3.mingw64-cross clean +make -C sqlite3 -f ../mf-sqlite3fts.mingw64-cross clean +make -C sqlite3 -f ../mf-sqlite3rtree.mingw64-cross clean +make -f mf-sqlite3extfunc.mingw64-cross clean + +echo "=============================" +echo "Building SQLite 2 ... ISO8859" +echo "=============================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -C sqlite -f ../mf-sqlite.mingw64-cross all +if test -n "$SQLITE_DLLS" ; then + $nov2 || make -C sqlite -f ../mf-sqlite.mingw64-cross sqlite.dll +fi + +echo "=================" +echo "Building zlib ..." +echo "=================" + +make -C zlib -f ../mf-zlib.mingw64-cross all + +echo "==========================" +echo "Building SQLite 2 ... UTF8" +echo "==========================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -C sqlite -f ../mf-sqlite.mingw64-cross clean +$nov2 || make -C sqlite -f ../mf-sqlite.mingw64-cross ENCODING=UTF8 all +if test -n "$SQLITE_DLLS" ; then + $nov2 || \ + make -C sqlite -f ../mf-sqlite.mingw64-cross ENCODING=UTF8 sqliteu.dll +fi + +echo "=====================" +echo "Building SQLite 3 ..." +echo "=====================" +make -C sqlite3 -f ../mf-sqlite3.mingw64-cross all +test -r sqlite3/tool/mksqlite3c.tcl && \ + make -C sqlite3 -f ../mf-sqlite3.mingw64-cross sqlite3.c +if test -r sqlite3/sqlite3.c -a -f "$WITH_SEE" ; then + cat sqlite3/sqlite3.c "$WITH_SEE" > sqlite3.c + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_HAS_CODEC=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_ACTIVATION_KEY=\\\"$SEE_KEY\\\"" + ADD_CFLAGS="$ADD_CFLAGS -DSEEEXT=\\\"$SEEEXT\\\"" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_API=static -DWIN32=1 -DNDEBUG=1 -DNO_TCL" + ADD_CFLAGS="$ADD_CFLAGS -DTHREADSAFE=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_DLL=1 -DSQLITE_THREADSAFE=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_OS_WIN=1 -DSQLITE_ASCII=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_SOUNDEX=1" + ADD_CFLAGS="$ADD_CFLAGS -DSQLITE_ENABLE_COLUMN_METADATA=1" + ADD_CFLAGS="$ADD_CFLAGS -DWITHOUT_SHELL=1" + export ADD_CFLAGS + ADD_NSIS="$ADD_NSIS -DWITHOUT_SQLITE3_EXE" + unset SQLITE3_A10N_O + unset SQLITE3_EXE +fi +if test -n "$SQLITE_DLLS" ; then + make -C sqlite3 -f ../mf-sqlite3.mingw64-cross sqlite3.dll +fi + +echo "===============================" +echo "Building ODBC drivers and utils" +echo "===============================" +if $nov2 ; then + make -f Makefile.mingw64-cross all_no2 +else + make -f Makefile.mingw64-cross +fi +make -f Makefile.mingw64-cross sqlite3odbc${SEEEXT}nw.dll + +echo "===================================" +echo "Building SQLite3 FTS extensions ..." +echo "===================================" +make -C sqlite3 -f ../mf-sqlite3fts.mingw64-cross clean all +mv sqlite3/sqlite3_mod_fts*.dll . + +echo "=====================================" +echo "Building SQLite3 rtree extensions ..." +echo "=====================================" +make -C sqlite3 -f ../mf-sqlite3rtree.mingw64-cross clean all +mv sqlite3/sqlite3_mod_rtree.dll . + +echo "========================================" +echo "Building SQLite3 extension functions ..." +echo "========================================" +make -f mf-sqlite3extfunc.mingw64-cross clean all + +echo "=========================" +echo "Building drivers ... UTF8" +echo "=========================" +( $nov2 && echo '*** skipped (NO_SQLITE2)' ) || true +$nov2 || make -f Makefile.mingw64-cross sqliteodbcu.dll sqliteu.exe + +echo "=======================" +echo "Cleanup after build ..." +echo "=======================" +$nov2 || make -C sqlite -f ../mf-sqlite.mingw64-cross clean +$nov2 || rm -f sqlite/sqlite.exe +make -C sqlite3 -f ../mf-sqlite3.mingw64-cross clean +make -C sqlite3 -f ../mf-sqlite3fts.mingw64-cross clean +make -C sqlite3 -f ../mf-sqlite3rtree.mingw64-cross clean +make -f mf-sqlite3extfunc.mingw64-cross semiclean + +echo "===========================" +echo "Creating NSIS installer ..." +echo "===========================" +cp -p README readme.txt +unix2dos < license.terms > license.txt || todos < license.terms > license.txt +makensis $ADD_NSIS sqliteodbc_w64.nsi + diff --git a/lang/sql/odbc/mkall.sh b/lang/sql/odbc/mkall.sh index 977e7bf5..3e98f6d6 100644 --- a/lang/sql/odbc/mkall.sh +++ b/lang/sql/odbc/mkall.sh @@ -15,23 +15,68 @@ else fi if test $(arch) = "x86_64" ; then - CC32="gcc -m32" + CC32="gcc -m32 -march=i386 -mtune=i386" SH32="linux32 sh" else CC32=gcc SH32=sh fi +case "$REL" in + [yYTt1-9]*) + MVER32="" + ;; + *) + MVER32="-$VER32" + ;; +esac + +export MSVCRT="" + +echo -n >/dev/tty "sqliteodbc_dl$MVER32.exe ... " NO_SQLITE2=1 NO_TCCEXT=1 SQLITE_DLLS=2 CC=$CC32 $SH32 mingw-cross-build.sh -mv sqliteodbc.exe dist/sqliteodbc_dl-$VER32.exe +mv sqliteodbc.exe dist/sqliteodbc_dl$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi +echo -n >/dev/tty "sqliteodbc$MVER32.exe ..." CC=$CC32 $SH32 mingw-cross-build.sh -mv sqliteodbc.exe dist/sqliteodbc-$VER32.exe +mv sqliteodbc.exe dist/sqliteodbc$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi -SQLITE_DLLS=2 sh mingw64-cross-build.sh -mv sqliteodbc_w64.exe dist/sqliteodbc_w64_dl-$VER32.exe +echo -n >/dev/tty "sqliteodbc_w64_dl$MVER32.exe ..." +NO_SQLITE2=1 SQLITE_DLLS=2 sh mingw64-cross-build.sh +mv sqliteodbc_w64.exe dist/sqliteodbc_w64_dl$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi +echo -n >/dev/tty "sqliteodbc_w64$MVER32.exe ..." sh mingw64-cross-build.sh -mv sqliteodbc_w64.exe dist/sqliteodbc_w64-$VER32.exe +mv sqliteodbc_w64.exe dist/sqliteodbc_w64$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + +export MSVCRT="100" +MVER32="_msvcr100$MVER32" +echo -n >/dev/tty "sqliteodbc_dl$MVER32.exe ..." +NO_SQLITE2=1 NO_TCCEXT=1 SQLITE_DLLS=2 CC=$CC32 $SH32 mingw-cross-build.sh +mv sqliteodbc.exe dist/sqliteodbc_dl$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + +echo -n >/dev/tty "sqliteodbc$MVER32.exe ..." +CC=$CC32 $SH32 mingw-cross-build.sh +mv sqliteodbc.exe dist/sqliteodbc$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + +echo -n >/dev/tty "sqliteodbc_w64_dl$MVER32.exe ..." +NO_SQLITE2=1 SQLITE_DLLS=2 sh mingw64-cross-build.sh +mv sqliteodbc_w64.exe dist/sqliteodbc_w64_dl$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + +echo -n >/dev/tty "sqliteodbc_w64$MVER32.exe ..." +sh mingw64-cross-build.sh +mv sqliteodbc_w64.exe dist/sqliteodbc_w64$MVER32.exe +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + +echo -n >/dev/tty "sqliteodbc-$VER.tar.gz ..." test -r ../sqliteodbc-$VER.tar.gz && cp -p ../sqliteodbc-$VER.tar.gz dist +if test $? = 0 ; then echo >/dev/tty OK ; else echo >/dev/tty ERROR ; fi + diff --git a/lang/sql/odbc/resource.h.in b/lang/sql/odbc/resource.h.in index ed742609..7923a6dd 100644 --- a/lang/sql/odbc/resource.h.in +++ b/lang/sql/odbc/resource.h.in @@ -23,6 +23,7 @@ #define IDC_FKSUPPORT 418 #define IDC_OEMCP 419 #define IDC_BIGINT 420 +#define IDC_JDCONV 421 #define IDS_MSGTITLE 500 #define IDS_BADDSN 501 #define IDS_EXTTITLE 502 diff --git a/lang/sql/odbc/resource3.h b/lang/sql/odbc/resource3.h index e12ba565..5ae8e368 100644 --- a/lang/sql/odbc/resource3.h +++ b/lang/sql/odbc/resource3.h @@ -1,5 +1,5 @@ -#define VERSION "0.93" -#define VERSION_C 0,93 +#define VERSION "0.996" +#define VERSION_C 0,996 #define DRIVERCONNECT 104 #define CONFIGDSN 105 #define IDC_DSNAME 400 @@ -23,6 +23,7 @@ #define IDC_FKSUPPORT 418 #define IDC_OEMCP 419 #define IDC_BIGINT 420 +#define IDC_JDCONV 421 #define IDS_MSGTITLE 500 #define IDS_BADDSN 501 #define IDS_EXTTITLE 502 diff --git a/lang/sql/odbc/sqlite.mak b/lang/sql/odbc/sqlite.mak new file mode 100644 index 00000000..6e88ada2 --- /dev/null +++ b/lang/sql/odbc/sqlite.mak @@ -0,0 +1,321 @@ +# VC++ 6.0 Makefile for SQLite 2.8.17 + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. + +TOP = ..\sqlite + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. + +BCC = cl -Gs -GX -D_WIN32 -nologo -Zi + +#### Leave MEMORY_DEBUG undefined for maximum speed. Use MEMORY_DEBUG=1 +# to check for memory leaks. Use MEMORY_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 + +#OPTS = -DMEMORY_DEBUG=2 +#OPTS = -DMEMORY_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. + +EXE = .exe + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. + +TCC = cl -Gs -GX -D_WIN32 -DOS_WIN=1 -nologo -Zi + +#### Should the database engine assume text is coded as UTF-8 or iso8859? + +!IF "$(ENCODING)" != "UTF8" +ENCODING = ISO8859 +!ENDIF + +# You should not have to change anything below this line +############################################################################### + +# This is how we compile + +TCCX = $(TCC) $(OPTS) -DWIN32=1 -DTHREADSAFE=1 -I. -I$(TOP)/src + +TCCXD = $(TCCX) -D_DLL + +# Object files for the SQLite library. + +LIBOBJ = attach.obj auth.obj btree.obj btree_rb.obj build.obj copy.obj \ + date.obj delete.obj expr.obj func.obj hash.obj insert.obj \ + main.obj os.obj pager.obj parse.obj pragma.obj printf.obj \ + random.obj select.obj \ + table.obj tokenize.obj trigger.obj update.obj util.obj \ + vacuum.obj vdbe.obj vdbeaux.obj where.obj \ + encode.obj opcodes.obj + +# All of the source code files. + +SRC = \ + $(TOP)/src/attach.c \ + $(TOP)/src/auth.c \ + $(TOP)/src/btree.c \ + $(TOP)/src/btree.h \ + $(TOP)/src/btree_rb.c \ + $(TOP)/src/build.c \ + $(TOP)/src/copy.c \ + $(TOP)/src/date.c \ + $(TOP)/src/delete.c \ + $(TOP)/src/expr.c \ + $(TOP)/src/hash.c \ + $(TOP)/src/hash.h \ + $(TOP)/src/insert.c \ + $(TOP)/src/main.c \ + $(TOP)/src/os.c \ + $(TOP)/src/pager.c \ + $(TOP)/src/pager.h \ + $(TOP)/src/parse.y \ + $(TOP)/src/pragma.c \ + $(TOP)/src/printf.c \ + $(TOP)/src/random.c \ + $(TOP)/src/select.c \ + $(TOP)/src/shell.c \ + $(TOP)/src/sqlite.h.in \ + $(TOP)/src/sqliteInt.h \ + $(TOP)/src/table.c \ + $(TOP)/src/func.c \ + $(TOP)/src/tclsqlite.c \ + $(TOP)/src/tokenize.c \ + $(TOP)/src/trigger.c \ + $(TOP)/src/update.c \ + $(TOP)/src/util.c \ + $(TOP)/src/vacuum.c \ + $(TOP)/src/vdbe.c \ + $(TOP)/src/vdbe.h \ + $(TOP)/src/vdbeaux.c \ + $(TOP)/src/vdbeInt.h \ + $(TOP)/src/where.c \ + $(TOP)/src/encode.c + +# Header files used by all library source files. + +HDR = \ + sqlite.h \ + $(TOP)/src/btree.h \ + config.h \ + $(TOP)/src/hash.h \ + opcodes.h \ + $(TOP)/src/os.h \ + $(TOP)/src/sqliteInt.h \ + $(TOP)/src/vdbe.h \ + parse.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. + +all: sqlite.h config.h sqlite.dll libsqlite.lib sqlite.exe + +sqlite.dll: $(LIBOBJ) sqlite.def + echo #include "sqlite.h" > version.c + echo const char sqlite_version[] = SQLITE_VERSION; >> version.c + echo #ifdef SQLITE_UTF8 >> version.c + echo const char sqlite_encoding[] = "UTF-8"; >> version.c + echo #else >> version.c + echo const char sqlite_encoding[] = "iso8859"; >> version.c + echo #endif >> version.c + $(TCCX) -c version.c + link -release -nodefaultlib -dll msvcrt.lib kernel32.lib \ + -def:$(TOP)\sqlite.def -out:$@ $(LIBOBJ) + lib sqlite.lib version.obj + +libsqlite.lib: $(LIBOBJ) + lib -out:$@ $(LIBOBJ) + +sqlite.exe: sqlite.dll + $(TCCX) -o $@ $(TOP)/src/shell.c sqlite.lib + +# Rules to build the LEMON compiler generator + +lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c + $(BCC) -o lemon $(TOP)/tool/lemon.c + copy $(TOP)\tool\lempar.c . + +attach.obj: $(TOP)/src/attach.c $(HDR) + $(TCCXD) -c $(TOP)/src/attach.c + +auth.obj: $(TOP)/src/auth.c $(HDR) + $(TCCXD) -c $(TOP)/src/auth.c + +btree.obj: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h + $(TCCXD) -c $(TOP)/src/btree.c + +btree_rb.obj: $(TOP)/src/btree_rb.c $(HDR) + $(TCCXD) -c $(TOP)/src/btree_rb.c + +build.obj: $(TOP)/src/build.c $(HDR) + $(TCCXD) -c $(TOP)/src/build.c + +copy.obj: $(TOP)/src/copy.c $(HDR) + $(TCCXD) -c $(TOP)/src/copy.c + +main.obj: $(TOP)/src/main.c $(HDR) + $(TCCXD) -c $(TOP)/src/main.c + +pager.obj: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h + $(TCCXD) -c $(TOP)/src/pager.c + +pragma.obj: $(TOP)/src/pragma.c $(HDR) + $(TCCXD) -c $(TOP)/src/pragma.c + +os.obj: $(TOP)/src/os.c $(HDR) + $(TCCXD) -c $(TOP)/src/os.c + +parse.obj: parse.c $(HDR) + $(TCCXD) -c parse.c + +parse.h: parse.c + +parse.c: $(TOP)/src/parse.y lemon + copy $(TOP)\src\parse.y . + .\lemon parse.y + +sqlite.h: $(TOP)/src/sqlite.h.in + ..\fixup < $(TOP)\src\sqlite.h.in > sqlite.h \ + --VERS-- @$(TOP)\VERSION --ENCODING-- $(ENCODING) + +config.h: + echo #include "stdio.h" >temp.c + echo int main(){printf( >>temp.c + echo "#define SQLITE_PTR_SZ %d\n",sizeof(char*)); >>temp.c + echo exit(0);} >>temp.c + $(BCC) -o temp temp.c + .\temp >config.h + @del temp.* + +opcodes.h: $(TOP)/src/vdbe.c + ..\mkopc <$(TOP)/src/vdbe.c + +tokenize.obj: $(TOP)/src/tokenize.c $(HDR) + $(TCCXD) -c $(TOP)/src/tokenize.c + +util.obj: $(TOP)/src/util.c $(HDR) + $(TCCXD) -c $(TOP)/src/util.c + +vdbe.obj: $(TOP)/src/vdbe.c $(HDR) + $(TCCXD) -c $(TOP)/src/vdbe.c + +vdbeaux.obj: $(TOP)/src/vdbeaux.c $(HDR) + $(TCCXD) -c $(TOP)/src/vdbeaux.c + +where.obj: $(TOP)/src/where.c $(HDR) + $(TCCXD) -c $(TOP)/src/where.c + +date.obj: $(TOP)/src/date.c $(HDR) + $(TCCXD) -c $(TOP)/src/date.c + +delete.obj: $(TOP)/src/delete.c $(HDR) + $(TCCXD) -c $(TOP)/src/delete.c + +expr.obj: $(TOP)/src/expr.c $(HDR) + $(TCCXD) -c $(TOP)/src/expr.c + +hash.obj: $(TOP)/src/hash.c $(HDR) + $(TCCXD) -c $(TOP)/src/hash.c + +insert.obj: $(TOP)/src/insert.c $(HDR) + $(TCCXD) -c $(TOP)/src/insert.c + +random.obj: $(TOP)/src/random.c $(HDR) + $(TCCXD) -c $(TOP)/src/random.c + +select.obj: $(TOP)/src/select.c $(HDR) + $(TCCXD) -c $(TOP)/src/select.c + +table.obj: $(TOP)/src/table.c $(HDR) + $(TCCXD) -c $(TOP)/src/table.c + +func.obj: $(TOP)/src/func.c $(HDR) + $(TCCXD) -c $(TOP)/src/func.c + +update.obj: $(TOP)/src/update.c $(HDR) + $(TCCXD) -c $(TOP)/src/update.c + +printf.obj: $(TOP)/src/printf.c $(HDR) + $(TCCXD) -c $(TOP)/src/printf.c + +encode.obj: $(TOP)/src/encode.c $(HDR) + $(TCCXD) -c $(TOP)/src/encode.c + +trigger.obj: $(TOP)/src/trigger.c $(HDR) + $(TCCXD) -c $(TOP)/src/trigger.c + +opcodes.obj: $(TOP)/opcodes.c $(HDR) + $(TCCXD) -c $(TOP)/opcodes.c + +vacuum.obj: $(TOP)/src/vacuum.c $(HDR) + $(TCCXD) -c $(TOP)/src/vacuum.c + +sqlite.def: sqlite.h + echo LIBRARY SQLITE > sqlite.def + echo DESCRIPTION 'SQLite Library' >> sqlite.def + echo EXPORTS >> sqlite.def + echo sqlite_open >> sqlite.def + echo sqlite_close >> sqlite.def + echo sqlite_exec >> sqlite.def + echo sqlite_last_insert_rowid >> sqlite.def + echo sqlite_changes >> sqlite.def + echo sqlite_error_string >> sqlite.def + echo sqlite_interrupt >> sqlite.def + echo sqlite_complete >> sqlite.def + echo sqlite_busy_handler >> sqlite.def + echo sqlite_busy_timeout >> sqlite.def + echo sqlite_get_table >> sqlite.def + echo sqlite_free_table >> sqlite.def + echo sqlite_exec_printf >> sqlite.def + echo sqlite_exec_vprintf >> sqlite.def + echo sqlite_get_table_printf >> sqlite.def + echo sqlite_get_table_vprintf >> sqlite.def + echo sqlite_freemem >> sqlite.def + echo sqlite_libversion >> sqlite.def + echo sqlite_libencoding >> sqlite.def + echo sqlite_create_function >> sqlite.def + echo sqlite_create_aggregate >> sqlite.def + echo sqlite_function_type >> sqlite.def + echo sqlite_set_result_string >> sqlite.def + echo sqlite_set_result_int >> sqlite.def + echo sqlite_set_result_double >> sqlite.def + echo sqlite_set_result_error >> sqlite.def + echo sqlite_user_data >> sqlite.def + echo sqlite_aggregate_context >> sqlite.def + echo sqlite_aggregate_count >> sqlite.def + echo sqlite_mprintf >> sqlite.def + echo sqliteStrICmp >> sqlite.def + echo sqlite_set_authorizer >> sqlite.def + echo sqlite_trace >> sqlite.def + echo sqlite_compile >> sqlite.def + echo sqlite_step >> sqlite.def + echo sqlite_finalize >> sqlite.def + echo sqlite_vmprintf >> sqlite.def + echo sqliteOsFileExists >> sqlite.def + echo sqliteIsNumber >> sqlite.def + echo sqliteStrNICmp >> sqlite.def + echo sqlite_encode_binary >> sqlite.def + echo sqlite_decode_binary >> sqlite.def + +clean: + del *.obj + del *.pdb + del *.dll + del *.lib + del *.exe + del sqlite.h + del opcodes.h + del opcodes.c + del config.h + del parse.h + del parse.c diff --git a/lang/sql/odbc/sqlite3.mak b/lang/sql/odbc/sqlite3.mak new file mode 100644 index 00000000..4ce10c9c --- /dev/null +++ b/lang/sql/odbc/sqlite3.mak @@ -0,0 +1,324 @@ +# VC++ 6.0 Makefile for SQLite 3.4.0 + +#### The toplevel directory of the source tree. This is the directory +# that contains this "Makefile.in" and the "configure.in" script. + +TOP = ..\sqlite3 + +#### C Compiler and options for use in building executables that +# will run on the platform that is doing the build. + +BCC = cl -Gs -GX -D_WIN32 -nologo -Zi + +#### Leave MEMORY_DEBUG undefined for maximum speed. Use MEMORY_DEBUG=1 +# to check for memory leaks. Use MEMORY_DEBUG=2 to print a log of all +# malloc()s and free()s in order to track down memory leaks. +# +# SQLite uses some expensive assert() statements in the inner loop. +# You can make the library go almost twice as fast if you compile +# with -DNDEBUG=1 + +#OPTS = -DMEMORY_DEBUG=2 +#OPTS = -DMEMORY_DEBUG=1 +#OPTS = +OPTS = -DNDEBUG=1 + +#### The suffix to add to executable files. ".exe" for windows. +# Nothing for unix. + +EXE = .exe + +#### C Compile and options for use in building executables that +# will run on the target platform. This is usually the same +# as BCC, unless you are cross-compiling. + +TCC = cl -Gs -GX -D_WIN32 -DOS_WIN=1 -nologo -Zi + +# You should not have to change anything below this line +############################################################################### + +# This is how we compile + +TCCX = $(TCC) $(OPTS) -DWIN32=1 -DTHREADSAFE=1 -DOS_WIN=1 \ + -DSQLITE_ENABLE_COLUMN_METADATA=1 -DSQLITE_SOUNDEX=1 \ + -DSQLITE_OMIT_LOAD_EXTENSION=1 -I. -I$(TOP)/src + +TCCXD = $(TCCX) -D_DLL + +# Object files for the SQLite library. + +LIBOBJ = alter.obj analyze.obj attach.obj auth.obj btree.obj \ + build.obj callback.obj complete.obj \ + date.obj delete.obj expr.obj func.obj hash.obj insert.obj \ + loadext.obj main.obj malloc.obj opcodes.obj os.obj os_win.obj \ + pager.obj parse.obj pragma.obj prepare.obj printf.obj random.obj \ + select.obj table.obj tokenize.obj trigger.obj update.obj \ + util.obj vacuum.obj vdbe.obj vdbeapi.obj vdbeaux.obj vdbeblob.obj \ + vdbefifo.obj vdbemem.obj \ + where.obj utf.obj legacy.obj vtab.obj + +# All of the source code files. + +SRC = \ + $(TOP)/src/alter.c \ + $(TOP)/src/analyze.c \ + $(TOP)/src/attach.c \ + $(TOP)/src/auth.c \ + $(TOP)/src/btree.c \ + $(TOP)/src/btree.h \ + $(TOP)/src/build.c \ + $(TOP)/src/callback.c \ + $(TOP)/src/complete.c \ + $(TOP)/src/date.c \ + $(TOP)/src/delete.c \ + $(TOP)/src/expr.c \ + $(TOP)/src/func.c \ + $(TOP)/src/hash.c \ + $(TOP)/src/hash.h \ + $(TOP)/src/insert.c \ + $(TOP)/src/legacy.c \ + $(TOP)/src/loadext.c \ + $(TOP)/src/main.c \ + $(TOP)/src/malloc.c \ + $(TOP)/src/os.c \ + $(TOP)/src/os_win.c \ + $(TOP)/src/pager.c \ + $(TOP)/src/pager.h \ + $(TOP)/src/parse.y \ + $(TOP)/src/pragma.c \ + $(TOP)/src/prepare.c \ + $(TOP)/src/printf.c \ + $(TOP)/src/random.c \ + $(TOP)/src/select.c \ + $(TOP)/src/shell.c \ + $(TOP)/src/sqlite.h.in \ + $(TOP)/src/sqlite3ext.h \ + $(TOP)/src/sqliteInt.h \ + $(TOP)/src/table.c \ + $(TOP)/src/tokenize.c \ + $(TOP)/src/trigger.c \ + $(TOP)/src/utf.c \ + $(TOP)/src/update.c \ + $(TOP)/src/util.c \ + $(TOP)/src/vacuum.c \ + $(TOP)/src/vdbe.c \ + $(TOP)/src/vdbe.h \ + $(TOP)/src/vdbeapi.c \ + $(TOP)/src/vdbeaux.c \ + $(TOP)/src/vdbeblob.c \ + $(TOP)/src/vdbefifo.c \ + $(TOP)/src/vdbemem.c \ + $(TOP)/src/vdbeInt.h \ + $(TOP)/src/vtab.c \ + $(TOP)/src/where.c + +# Header files used by all library source files. + +HDR = \ + sqlite3.h \ + $(TOP)/src/btree.h \ + $(TOP)/src/btreeInt.h \ + $(TOP)/src/hash.h \ + $(TOP)/src/limits.h \ + opcodes.h \ + $(TOP)/src/os.h \ + $(TOP)/src/os_common.h \ + $(TOP)/src/sqlite3ext.h \ + $(TOP)/src/sqliteInt.h \ + $(TOP)/src/vdbe.h \ + parse.h + +# Header files used by the VDBE submodule + +VDBEHDR = \ + $(HDR) \ + $(TOP)/src/vdbeInt.h + +# This is the default Makefile target. The objects listed here +# are what get build when you type just "make" with no arguments. + +all: sqlite3.h config.h sqlite3.dll libsqlite3.lib sqlite3.exe + +sqlite3.dll: $(LIBOBJ) $(TOP)/sqlite3.def + echo #include "sqlite3.h" > version.c + echo const char sqlite3_version[] = SQLITE_VERSION; >> version.c + $(TCCX) -c version.c + link -release -nodefaultlib -dll msvcrt.lib kernel32.lib \ + -def:$(TOP)\sqlite3.def -out:$@ $(LIBOBJ) + lib sqlite3.lib version.obj + +libsqlite3.lib: $(LIBOBJ) + lib -out:$@ $(LIBOBJ) + +sqlite3.exe: sqlite3.dll + $(TCCX) -o $@ $(TOP)/src/shell.c sqlite3.lib + +# Rules to build the LEMON compiler generator + +lemon: $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c + $(BCC) -o lemon $(TOP)/tool/lemon.c + copy $(TOP)\tool\lempar.c . + +keywordhash.h: $(TOP)/tool/mkkeywordhash.c + $(BCC) -o mkkwhash $(OPTS) $(TOP)/tool/mkkeywordhash.c + .\mkkwhash > keywordhash.h + +alter.obj: $(TOP)/src/alter.c $(HDR) + $(TCCXD) -c $(TOP)/src/alter.c + +analyze.obj: $(TOP)/src/analyze.c $(HDR) + $(TCCXD) -c $(TOP)/src/analyze.c + +attach.obj: $(TOP)/src/attach.c $(HDR) + $(TCCXD) -c $(TOP)/src/attach.c + +auth.obj: $(TOP)/src/auth.c $(HDR) + $(TCCXD) -c $(TOP)/src/auth.c + +btree.obj: $(TOP)/src/btree.c $(HDR) $(TOP)/src/pager.h + $(TCCXD) -c $(TOP)/src/btree.c + +build.obj: $(TOP)/src/build.c $(HDR) + $(TCCXD) -c $(TOP)/src/build.c + +callback.obj: $(TOP)/src/callback.c $(HDR) + $(TCCXD) -c $(TOP)/src/callback.c + +complete.obj: $(TOP)/src/complete.c $(HDR) + $(TCCXD) -c $(TOP)/src/complete.c + +date.obj: $(TOP)/src/date.c $(HDR) + $(TCCXD) -c $(TOP)/src/date.c + +delete.obj: $(TOP)/src/delete.c $(HDR) + $(TCCXD) -c $(TOP)/src/delete.c + +expr.obj: $(TOP)/src/expr.c $(HDR) + $(TCCXD) -c $(TOP)/src/expr.c + +func.obj: $(TOP)/src/func.c $(HDR) + $(TCCXD) -c $(TOP)/src/func.c + +hash.obj: $(TOP)/src/hash.c $(HDR) + $(TCCXD) -c $(TOP)/src/hash.c + +insert.obj: $(TOP)/src/insert.c $(HDR) + $(TCCXD) -c $(TOP)/src/insert.c + +legacy.obj: $(TOP)/src/legacy.c $(HDR) + $(TCCXD) -c $(TOP)/src/legacy.c + +loadext.obj: $(TOP)/src/loadext.c $(HDR) + $(TCCXD) -c $(TOP)/src/loadext.c + +main.obj: $(TOP)/src/main.c $(HDR) + $(TCCXD) -c $(TOP)/src/main.c + +malloc.obj: $(TOP)/src/malloc.c $(HDR) + $(TCCXD) -c $(TOP)/src/malloc.c + +pager.obj: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h + $(TCCXD) -c $(TOP)/src/pager.c + +opcodes.obj: opcodes.c $(HDR) + $(TCCXD) -c opcodes.c + +os.obj: $(TOP)/src/os.c $(HDR) + $(TCCXD) -c $(TOP)/src/os.c + +os_win.obj: $(TOP)/src/os_win.c $(HDR) + $(TCCXD) -c $(TOP)/src/os_win.c + +parse.h: parse.c + +parse.obj: parse.c $(HDR) + $(TCCXD) -c parse.c + +parse.c: $(TOP)/src/parse.y lemon + copy $(TOP)\src\parse.y . + .\lemon parse.y + +pragma.obj: $(TOP)/src/pragma.c $(HDR) + $(TCCXD) -c $(TOP)/src/pragma.c + +prepare.obj: $(TOP)/src/prepare.c $(HDR) + $(TCCXD) -c $(TOP)/src/prepare.c + +printf.obj: $(TOP)/src/printf.c $(HDR) + $(TCCXD) -c $(TOP)/src/printf.c + +random.obj: $(TOP)/src/random.c $(HDR) + $(TCCXD) -c $(TOP)/src/random.c + +select.obj: $(TOP)/src/select.c $(HDR) + $(TCCXD) -c $(TOP)/src/select.c + +table.obj: $(TOP)/src/table.c $(HDR) + $(TCCXD) -c $(TOP)/src/table.c + +tokenize.obj: $(TOP)/src/tokenize.c keywordhash.h $(HDR) + $(TCCXD) -c $(TOP)/src/tokenize.c + +trigger.obj: $(TOP)/src/trigger.c $(HDR) + $(TCCXD) -c $(TOP)/src/trigger.c + +update.obj: $(TOP)/src/update.c $(HDR) + $(TCCXD) -c $(TOP)/src/update.c + +utf.obj: $(TOP)/src/utf.c $(HDR) + $(TCCXD) -c $(TOP)/src/utf.c + +util.obj: $(TOP)/src/util.c $(HDR) + $(TCCXD) -c $(TOP)/src/util.c + +vacuum.obj: $(TOP)/src/vacuum.c $(HDR) + $(TCCXD) -c $(TOP)/src/vacuum.c + +vdbe.obj: $(TOP)/src/vdbe.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbe.c + +vdbeapi.obj: $(TOP)/src/vdbeapi.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbeapi.c + +vdbeaux.obj: $(TOP)/src/vdbeaux.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbeaux.c + +vdbeblob.obj: $(TOP)/src/vdbeblob.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbeblob.c + +vdbefifo.obj: $(TOP)/src/vdbefifo.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbefifo.c + +vdbemem.obj: $(TOP)/src/vdbemem.c $(VDBEHDR) + $(TCCXD) -c $(TOP)/src/vdbemem.c + +vtab.obj: $(TOP)/src/vtab.c $(HDR) + $(TCCXD) -c $(TOP)/src/vtab.c + +where.obj: $(TOP)/src/where.c $(HDR) + $(TCCXD) -c $(TOP)/src/where.c + +sqlite3.h: $(TOP)/src/sqlite.h.in + ..\fixup < $(TOP)\src\sqlite.h.in > sqlite3.h \ + --VERS-- @$(TOP)\VERSION \ + --VERSION-NUMBER-- @@$(TOP)\VERSION + +opcodes.h: $(TOP)/src/vdbe.c parse.h + ..\mkopc3 <$(TOP)/src/vdbe.c parse.h + ..\fixup < opcodes.c > opcodes.new \ + sqliteOpcodeNames sqlite3OpcodeNames + del opcodes.c + ren opcodes.new opcodes.c + +clean: + del *.obj + del *.pdb + del *.dll + del *.lib + del *.exe + del sqlite3.h + del keywordhash.h + del opcodes.h + del opcodes.c + del parse.h + del parse.c diff --git a/lang/sql/odbc/sqlite3odbc.c b/lang/sql/odbc/sqlite3odbc.c index 22b93158..6cabb323 100644 --- a/lang/sql/odbc/sqlite3odbc.c +++ b/lang/sql/odbc/sqlite3odbc.c @@ -2,9 +2,9 @@ * @file sqlite3odbc.c * SQLite3 ODBC Driver main module. * - * $Id: sqlite3odbc.c,v 1.136 2011/11/12 04:35:40 chw Exp chw $ + * $Id: sqlite3odbc.c,v 1.162 2013/12/08 07:18:56 chw Exp chw $ * - * Copyright (c) 2004-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2004-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a @@ -19,6 +19,7 @@ #if defined(WITH_SQLITE_DLLS) && (WITH_SQLITE_DLLS > 1) #define SQLITE_DYNLOAD 1 +#undef HAVE_SQLITE3CLOSEV2 #endif #include "sqlite3odbc.h" @@ -41,7 +42,7 @@ static struct dl_sqlite3_funcs { void (*p4)(void *)); int (*bind_double)(sqlite3_stmt *p0, int p1, double p2); int (*bind_int)(sqlite3_stmt *p0, int p1, int p2); - int (*bind_int64)(sqlite3_stmt *p0, int p1, sqlite3_int64 p2); + int (*bind_int64)(sqlite3_stmt *p0, int p1, sqlite_int64 p2); int (*bind_null)(sqlite3_stmt *p0, int p1); int (*bind_parameter_count)(sqlite3_stmt *p0); int (*bind_text)(sqlite3_stmt *p0, int p1, const char *p2, int p3, @@ -54,6 +55,7 @@ static struct dl_sqlite3_funcs { int (*column_count)(sqlite3_stmt *p0); const char * (*column_database_name)(sqlite3_stmt *p0, int p1); const char * (*column_decltype)(sqlite3_stmt *p0, int p1); + double (*column_double)(sqlite3_stmt *p0, int p1); const char * (*column_name)(sqlite3_stmt *p0, int p1); const char * (*column_origin_name)(sqlite3_stmt *p0, int p1); const char * (*column_table_name)(sqlite3_stmt *p0, int p1); @@ -77,6 +79,7 @@ static struct dl_sqlite3_funcs { int *p3, int *p4, char **p5); void (*interrupt)(sqlite3 *p0); int (*key)(sqlite3 *p0, const void *p1, int p2); + sqlite_int64 (*last_insert_rowid)(sqlite3 *p0); const char * (*libversion)(void); int (*load_extension)(sqlite3 *p0, const char *p1, const char *p2, char **p3); @@ -130,6 +133,7 @@ static struct dl_sqlite3_funcs { #define sqlite3_column_count dls_funcs.column_count #define sqlite3_column_database_name dls_funcs.column_database_name #define sqlite3_column_decltype dls_funcs.column_decltype +#define sqlite3_column_double dls_funcs.column_double #define sqlite3_column_name dls_funcs.column_name #define sqlite3_column_origin_name dls_funcs.column_origin_name #define sqlite3_column_table_name dls_funcs.column_table_name @@ -146,6 +150,7 @@ static struct dl_sqlite3_funcs { #define sqlite3_get_table dls_funcs.get_table #define sqlite3_interrupt dls_funcs.interrupt #define sqlite3_key dls_funcs.key +#define sqlite3_last_insert_rowid dls_funcs.last_insert_rowid #define sqlite3_libversion dls_funcs.libversion #define sqlite3_load_extension dls_funcs.load_extension #define sqlite3_malloc dls_funcs.malloc @@ -246,6 +251,19 @@ static struct dl_sqlite3_funcs { #define DBC_MAGIC 0x53544144 #define DEAD_MAGIC 0xdeadbeef +/** + * @typedef dstr + * @struct dstr + * Internal structure representing dynamic strings. + */ + +typedef struct dstr { + int len; /**< Current length. */ + int max; /**< Maximum length of buffer. */ + int oom; /**< True when out of memory. */ + char buffer[1]; /**< String buffer. */ +} dstr; + static const char *xdigits = "0123456789ABCDEFabcdef"; #ifdef MEMORY_DEBUG @@ -383,6 +401,7 @@ xstrdup_(const char *str, char *file, int line) #define snprintf _snprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp +#define strtoll _strtoi64 static HINSTANCE NEAR hModule; /* Saved module handle for resources */ @@ -568,6 +587,9 @@ static SQLRETURN setupdyncols(STMT *s, sqlite3_stmt *s3stmt, int *ncolsp); static SQLRETURN setupparbuf(STMT *s, BINDPARM *p); static SQLRETURN starttran(STMT *s); static SQLRETURN setupparam(STMT *s, char *sql, int pnum); +static SQLRETURN getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, + SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, + int partial); #if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) /* MS Access hack part 1 (reserved error -7748) */ @@ -596,12 +618,169 @@ strdup_(const char *str) } #endif +/** + * Append string to dynamic string. + * @param dsp dstr pointer + * @param str string to append + * @result dsp result dstr pointer or NULL. + */ + +static dstr * +dsappend(dstr *dsp, const char *str) +{ + int len; + + if (!str) { + return dsp; + } + len = strlen(str); + if (!dsp) { + int max = 256; + + if (max < len) { + max += len; + } + dsp = xmalloc(max); + if (dsp) { + dsp->max = max; + dsp->len = dsp->oom = 0; + goto copy; + } + return dsp; + } + if (dsp->oom) { + return dsp; + } + if (dsp->len + len > dsp->max) { + int max = dsp->max + len + 256; + dstr *ndsp = xrealloc(dsp, max); + + if (!ndsp) { + strcpy(dsp->buffer, "OUT OF MEMORY"); + dsp->max = dsp->len = 13; + dsp->oom = 1; + return dsp; + } + dsp = ndsp; + dsp->max = max; + } +copy: + strcpy(dsp->buffer + dsp->len, str); + dsp->len += len; + return dsp; +} + +/** + * Append string quoted to dynamic string. + * @param dsp dstr pointer + * @param str string to append + * @result dsp result dstr pointer or NULL. + */ + +static dstr * +dsappendq(dstr *dsp, const char *str) +{ + int len; + const char *p; + char *q; + + if (!str) { + return dsp; + } + len = strlen(str); + for (p = str; *p; ++p) { + if (p[0] == '"') { + ++len; + } + } + if (!dsp) { + int max = 256; + + if (max < len) { + max += len; + } + dsp = xmalloc(max); + if (dsp) { + dsp->max = max; + dsp->len = dsp->oom = 0; + goto copy; + } + return dsp; + } + if (dsp->oom) { + return dsp; + } + if (dsp->len + len > dsp->max) { + int max = dsp->max + len + 256; + dstr *ndsp = xrealloc(dsp, max); + + if (!ndsp) { + strcpy(dsp->buffer, "OUT OF MEMORY"); + dsp->max = dsp->len = 13; + dsp->oom = 1; + return dsp; + } + dsp = ndsp; + dsp->max = max; + } +copy: + for (p = str, q = dsp->buffer + dsp->len; *p; ++p) { + *q++ = *p; + if (p[0] == '"') { + *q++ = '"'; + } + } + *q = '\0'; + dsp->len += len; + return dsp; +} + +/** + * Return dynamic string's value. + * @param dsp dstr pointer + * @result string value + */ + +static const char * +dsval(dstr *dsp) +{ + if (dsp) { + return (const char *) dsp->buffer; + } + return "ERROR"; +} + +/** + * Check error on dynamic string. + * @param dsp dstr pointer + * @result true when error pending + */ + +static int +dserr(dstr *dsp) +{ + return !dsp || dsp->oom; +} + +/** + * Free dynamic string. + * @param dsp dstr pointer + */ + +static void +dsfree(dstr *dsp) +{ + if (dsp) { + xfree(dsp); + } +} + #ifdef WCHARSUPPORT /** * Return length of UNICODE string. * @param str UNICODE string - * @result length of string + * @result length of string in characters */ static int @@ -622,7 +801,7 @@ uc_strlen(SQLWCHAR *str) * Copy UNICODE string like strncpy(). * @param dest destination area * @param src source area - * @param len length of source area + * @param len length of source area in characters * @return pointer to destination area */ @@ -647,7 +826,7 @@ uc_strncpy(SQLWCHAR *dest, SQLWCHAR *src, int len) /** * Make UNICODE string from UTF8 string into buffer. * @param str UTF8 string to be converted - * @param len length of str or -1 + * @param len length in characters of str or -1 * @param uc destination area to receive UNICODE string * @param ucLen byte length of destination area */ @@ -701,16 +880,16 @@ uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen) (str[3] & 0xc0) == 0x80) { unsigned long t = ((c & 0x03) << 18) | ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | - (str[4] & 0x3f); + (str[3] & 0x3f); if (sizeof (SQLWCHAR) == 2 * sizeof (char) && t >= 0x10000) { t -= 0x10000; - uc[i++] = 0xd800 | (t & 0x3ff); + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); if (i >= ucLen) { break; } - t = 0xdc00 | ((t >> 10) & 0x3ff); + t = 0xdc00 | (t & 0x3ff); } uc[i++] = t; str += 4; @@ -723,16 +902,16 @@ uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen) (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80) { unsigned long t = ((c & 0x01) << 24) | ((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) | - ((str[4] & 0x3f) << 6) | (str[5] & 0x3f); + ((str[3] & 0x3f) << 6) | (str[4] & 0x3f); if (sizeof (SQLWCHAR) == 2 * sizeof (char) && t >= 0x10000) { t -= 0x10000; - uc[i++] = 0xd800 | (t & 0x3ff); + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); if (i >= ucLen) { break; } - t = 0xdc00 | ((t >> 10) & 0x3ff); + t = 0xdc00 | (t & 0x3ff); } uc[i++] = t; str += 5; @@ -819,8 +998,8 @@ uc_to_utf(SQLWCHAR *str, int len) c >= 0xd800 && c <= 0xdbff && i + 1 < len) { unsigned long c2 = str[i + 1] & 0xffff; - if (c2 >= 0xdc00 && c <= 0xdfff) { - c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000; + if (c2 >= 0xdc00 && c2 <= 0xdfff) { + c = (((c & 0x3ff) << 10) | (c2 & 0x3ff)) + 0x10000; *cp++ = 0xf0 | ((c >> 18) & 0x07); *cp++ = 0x80 | ((c >> 12) & 0x3f); *cp++ = 0x80 | ((c >> 6) & 0x3f); @@ -1204,22 +1383,23 @@ s3bind(DBC *d, sqlite3_stmt *stmt, int nparams, BINDPARM *p) } } -/* +/** + * @typedef TBLRES + * @struct tblres * Internal structure for managing driver's * sqlite3_get_table() implementation. */ typedef struct tblres { - char **resarr; - char *errmsg; - sqlite3_stmt *stmt; - STMT *s; - int nres; - int nalloc; - int nrow; - int ncol; - PTRDIFF_T ndata; - int rc; + char **resarr; /**< result array */ + char *errmsg; /**< error message or NULL */ + sqlite3_stmt *stmt; /**< SQLite3 statement pointer */ + STMT *s; /**< Driver statement pointer */ + int nalloc; /**< alloc'ed size of result array */ + int nrow; /**< number of rows in result array */ + int ncol; /**< number of columns in result array */ + PTRDIFF_T ndata; /**< index into result array */ + int rc; /**< SQLite return code */ } TBLRES; /* @@ -1306,6 +1486,33 @@ nomem: } *qp++ = '\''; *qp = '\0'; +#ifdef _MSC_VER + } else if (coltype == SQLITE_FLOAT) { + static struct lconv *lc = 0; + double val = sqlite3_column_double(t->stmt, i); + char buffer[128]; + + /* + * This avoids floating point rounding + * and formatting problems of some SQLite + * versions in conjunction with MSVC 2010. + */ + snprintf(buffer, sizeof (buffer), "%.15g", val); + if (!lc) { + lc = localeconv(); + } + if (lc && lc->decimal_point && lc->decimal_point[0] && + lc->decimal_point[0] != '.') { + p = strchr(buffer, lc->decimal_point[0]); + if (p) { + *p = '.'; + } + } + p = xstrdup(buffer); + if (!p) { + goto nomem; + } +#endif } else if (coltype != SQLITE_NULL) { p = xstrdup((char *) sqlite3_column_text(t->stmt, i)); if (!p) { @@ -1340,7 +1547,6 @@ drvgettable(STMT *s, const char *sql, char ***resp, int *nrowp, *ncolp = 0; } tres.errmsg = NULL; - tres.nres = 0; tres.nrow = 0; tres.ncol = 0; tres.ndata = 1; @@ -1655,11 +1861,13 @@ noconn(STMT *s) static double ln_strtod(const char *data, char **endp) { - struct lconv *lc; + static struct lconv *lc = 0; char buf[128], *p, *end; double value; - lc = localeconv(); + if (!lc) { + lc = localeconv(); + } if (lc && lc->decimal_point && lc->decimal_point[0] && lc->decimal_point[0] != '.') { strncpy(buf, data, sizeof (buf) - 1); @@ -1750,7 +1958,7 @@ unescpat(char *str) * SQL LIKE string match with optional backslash escape handling. * @param str string * @param pat pattern - * @param esc when true, treat literally "\\" as "\", "\?" as "?", "\_" as "_" + * @param esc when true, treat literally "\\" as "\", "\%" as "%", "\_" as "_" * @result true when pattern matched */ @@ -2277,7 +2485,7 @@ mapdeftype(int type, int stype, int nosign, int nowchar) * @param sql original query string * @param sqlLen length of query string or SQL_NTS * @param nparam output number of parameters - * @param isselect output indicator for SELECT statement + * @param isselect output indicator for SELECT (1) or DDL statement (2) * @param errmsg output error message * @result newly allocated string containing query string for SQLite or NULL */ @@ -2288,7 +2496,9 @@ fixupsql(char *sql, int sqlLen, int *nparam, int *isselect, char **errmsg) char *q = sql, *qz = NULL, *p, *inq = NULL, *out; int np = 0, isddl = -1, size; - *errmsg = NULL; + if (errmsg) { + *errmsg = NULL; + } if (sqlLen != SQL_NTS) { qz = q = xmalloc(sqlLen + 1); if (!qz) { @@ -2347,14 +2557,37 @@ errout: ++qq; } if (*qq && *qq != ';') { + int i; + static const struct { + int len; + const char *str; + } ddlstr[] = { + { 5, "alter" }, + { 7, "analyze" }, + { 6, "attach" }, + { 5, "begin" }, + { 6, "commit" }, + { 6, "create" }, + { 6, "detach" }, + { 4, "drop" }, + { 3, "end" }, + { 7, "reindex" }, + { 7, "release" }, + { 8, "rollback" }, + { 9, "savepoint" }, + { 6, "vacuum" } + }; + size = strlen(qq); - if ((size >= 5) && - (strncasecmp(qq, "create", 5) == 0)) { - isddl = 1; - } else if ((size >= 4) && - (strncasecmp(qq, "drop", 4) == 0)) { - isddl = 1; - } else { + for (i = 0; i < array_size(ddlstr); i++) { + if (size >= ddlstr[i].len && + strncasecmp(qq, ddlstr[i].str, ddlstr[i].len) + == 0) { + isddl = 1; + break; + } + } + if (isddl != 1) { isddl = 0; } } @@ -2367,7 +2600,9 @@ errout: } while (*qq && ISSPACE(*qq)); if (*qq && *qq != ';') { freep(&out); - *errmsg = "only one SQL statement allowed"; + if (errmsg) { + *errmsg = "only one SQL statement allowed"; + } goto errout; } } @@ -2375,30 +2610,78 @@ errout: *p++ = *q; break; case '{': - /* deal with {d 'YYYY-MM-DD'}, {t ...}, and {ts ...} */ + /* + * Deal with escape sequences: + * {d 'YYYY-MM-DD'}, {t ...}, {ts ...} + * {oj ...}, {fn ...} etc. + */ if (!inq) { - char *end = q + 1; + int ojfn = 0, brc = 0; + char *inq2 = NULL, *end = q + 1, *start; - while (*end && *end != '}') { + while (*end && ISSPACE(*end)) { + ++end; + } + if (*end != 'd' && *end != 'D' && + *end != 't' && *end != 'T') { + ojfn = 1; + } + start = end; + while (*end) { + if (inq2 && *end == *inq2) { + inq2 = NULL; + } else if (inq2 == NULL && *end == '{') { + char *nerr = 0, *nsql; + + nsql = fixupsql(end, SQL_NTS, 0, 0, &nerr); + if (nsql && !nerr) { + strcpy(end, nsql); + } else { + brc++; + } + freep(&nsql); + } else if (inq2 == NULL && *end == '}') { + if (brc-- <= 0) { + break; + } + } else if (inq2 == NULL && (*end == '\'' || *end == '"')) { + inq2 = end; + } else if (inq2 == NULL && *end == '?') { + np++; + } ++end; } if (*end == '}') { - char *start = q + 1; char *end2 = end - 1; - while (start < end2 && *start != '\'') { - ++start; - } - while (end2 > start && *end2 != '\'') { - --end2; - } - if (*start == '\'' && *end2 == '\'') { - while (start <= end2) { + if (ojfn) { + while (start < end) { + if (ISSPACE(*start)) { + break; + } + ++start; + } + while (start < end) { *p++ = *start; ++start; } q = end; break; + } else { + while (start < end2 && *start != '\'') { + ++start; + } + while (end2 > start && *end2 != '\'') { + --end2; + } + if (*start == '\'' && *end2 == '\'') { + while (start <= end2) { + *p++ = *start; + ++start; + } + q = end; + break; + } } } } @@ -2415,7 +2698,7 @@ errout: } if (isselect) { if (isddl > 0) { - *isselect = 0; + *isselect = 2; } else { int incom = 0; @@ -2448,7 +2731,16 @@ errout: p++; } size = strlen(p); - *isselect = (size >= 6) && (strncasecmp(p, "select", 6) == 0); + if (size >= 6 && + (strncasecmp(p, "select", 6) == 0 || + strncasecmp(p, "pragma", 6) == 0)) { + *isselect = 1; + } else if (size >= 7 && + strncasecmp(p, "explain", 7) == 0) { + *isselect = 1; + } else { + *isselect = 0; + } } } return out; @@ -2496,9 +2788,9 @@ findcol(char **cols, int ncols, char *name) static void fixupdyncols(STMT *s, DBC *d) { - int i; + int i, k; #if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !(HAVE_SQLITE3TABLECOLUMNMETADATA) - int k, pk, nn, t, r, nrows, ncols; + int pk, nn, t, r, nrows, ncols; char **rowp, *flagp, flags[128]; #endif @@ -2608,7 +2900,7 @@ fixupdyncols(STMT *s, DBC *d) strcmp(s->dyncols[m].table, s->dyncols[i].table) == 0) { char *typename = rowp[r * ncols + t]; - flagp[m] = 1; + flagp[m] = i + 1; freep(&s->dyncols[m].typename); s->dyncols[m].typename = xstrdup(typename); s->dyncols[m].type = @@ -2636,6 +2928,7 @@ fixupdyncols(STMT *s, DBC *d) s->dyncols[i].type = SQL_LONGVARBINARY; } if (pk >= 0 && strcmp(rowp[r * ncols + pk], "1") == 0) { + s->dyncols[m].ispk = 1; if (++autoinccount > 1) { if (lastpk >= 0) { s->dyncols[lastpk].autoinc = SQL_FALSE; @@ -2648,6 +2941,8 @@ fixupdyncols(STMT *s, DBC *d) s->dyncols[m].autoinc = SQL_TRUE; } } + } else { + s->dyncols[m].ispk = 0; } if (nn >= 0 && rowp[r * ncols + nn][0] != '0') { s->dyncols[m].notnull = SQL_NO_NULLS; @@ -2658,13 +2953,109 @@ fixupdyncols(STMT *s, DBC *d) freet: sqlite3_free_table(rowp); } + for (i = k = 0; i < s->dcols; i++) { + if (flagp[i] == 0) { + break; + } + if (k == 0) { + k = flagp[i]; + } else if (flagp[i] != k) { + k = 0; + break; + } + } + s->one_tbl = k ? 1 : 0; + k = 0; + if (s->one_tbl) { + for (i = 0; i < s->dcols; i++) { + if (s->dyncols[i].ispk > 0) { + ++k; + } + } + } + s->has_pk = k; if (flagp != flags) { freep(&flagp); } +#else + for (i = 1, k = 0; i < s->dcols; i++) { + if (strcmp(s->dyncols[i].table, s->dyncols[0].table) == 0) { + k++; + } + } + s->one_tbl = (k && k + 1 == s->dcols) ? 1 : 0; + k = 0; + if (s->one_tbl) { + for (i = 0; i < s->dcols; i++) { + if (s->dyncols[i].ispk > 0) { + ++k; + if (s->has_rowid < 0 && s->dyncols[i].isrowid > 0) { + s->has_rowid = i; + } + } + } + } + s->has_pk = k; #endif } /** + * Convert julian day to year/month/day. + * @param jd julian day as stored in database + * @param ds output DATE_STRUCT + */ + +static void +convJD2YMD(double jd, DATE_STRUCT *ds) +{ + int z, a, b, c, d, e, x1; + sqlite_int64 ijd; + + ijd = jd * 86400000.0 + 0.5; + z = (int) ((ijd + 43200000) / 86400000); + a = (int) ((z - 1867216.25) / 36524.25); + a = z + 1 + a - (a / 4); + b = a + 1524; + c = (int) ((b - 122.1) / 365.25); + d = (36525 * c) / 100; + e = (int) ((b - d) / 30.6001); + x1 = (int) (30.6001 * e); + ds->day = b - d - x1; + ds->month = (e < 14) ? (e - 1) : (e - 13); + ds->year = (ds->month > 2) ? (c - 4716) : (c - 4715); +} + + +/** + * Convert julian day to hour/minute/second. + * @param jd julian day as stored in database + * @param ts output TIME_STRUCT + * @param fp optional fractional part output + */ + +static void +convJD2HMS(double jd, TIME_STRUCT *ts, int *fp) +{ + int s; + double ds; + sqlite_int64 ijd; + + ijd = jd * 86400000.0 + 0.5; + s = (int)((ijd + 43200000) % 86400000); + ds = s / 1000.0; + if (fp) { + *fp = (s % 1000) * 1000000; + } + s = (int) ds; + ds -= s; + ts->hour = s / 3600; + s -= ts->hour * 3600; + ts->minute = s / 60; + ds += s - ts->minute *60; + ts->second = (int) ds; +} + +/** * Return number of month days. * @param year * @param month 1..12 @@ -2692,6 +3083,7 @@ getmdays(int year, int month) /** * Convert string to ODBC DATE_STRUCT. + * @param jdconv when true, allow julian day format * @param str string to be converted * @param ds output DATE_STRUCT * @result 0 on success, -1 on error @@ -2699,15 +3091,31 @@ getmdays(int year, int month) * Strings of the format 'YYYYMMDD' or 'YYYY-MM-DD' or * 'YYYY/MM/DD' or 'MM/DD/YYYY' are converted to a * DATE_STRUCT. + * + * If the string looks like a floating point number, + * SQLite3's julian day format is assumed. */ static int -str2date(char *str, DATE_STRUCT *ds) +str2date(int jdconv, char *str, DATE_STRUCT *ds) { int i, err = 0; + double jd; char *p, *q, sepc = '\0'; ds->year = ds->month = ds->day = 0; + if (jdconv) { + p = strchr(str, '.'); + if (p) { + /* julian day format */ + p = 0; + jd = ln_strtod(str, &p); + if (p && p > str) { + convJD2YMD(jd, ds); + return 0; + } + } + } p = str; while (*p && !ISDIGIT(*p)) { ++p; @@ -2791,21 +3199,38 @@ done: /** * Convert string to ODBC TIME_STRUCT. + * @param jdconv when true, allow julian day format * @param str string to be converted * @param ts output TIME_STRUCT * @result 0 on success, -1 on error * * Strings of the format 'HHMMSS' or 'HH:MM:SS' * are converted to a TIME_STRUCT. + * + * If the string looks like a floating point number, + * SQLite3's julian day format is assumed. */ static int -str2time(char *str, TIME_STRUCT *ts) +str2time(int jdconv, char *str, TIME_STRUCT *ts) { int i, err = 0, ampm = -1; + double jd; char *p, *q; ts->hour = ts->minute = ts->second = 0; + if (jdconv) { + p = strchr(str, '.'); + if (p) { + /* julian day format */ + p = 0; + jd = ln_strtod(str, &p); + if (p && p > str) { + convJD2HMS(jd, ts, 0); + return 0; + } + } + } p = str; while (*p && !ISDIGIT(*p)) { ++p; @@ -2890,6 +3315,7 @@ done: /** * Convert string to ODBC TIMESTAMP_STRUCT. + * @param jdconv when true, allow julian day format * @param str string to be converted * @param tss output TIMESTAMP_STRUCT * @result 0 on success, -1 on error @@ -2901,17 +3327,56 @@ done: * YYYY-MM-DDThh:mm:ss[.f]shh:mm * are also supported. In case a time zone field is present, * the resulting TIMESTAMP_STRUCT is expressed in UTC. + * + * If the string looks like a floating point number, + * SQLite3's julian day format is assumed. */ static int -str2timestamp(char *str, TIMESTAMP_STRUCT *tss) +str2timestamp(int jdconv, char *str, TIMESTAMP_STRUCT *tss) { int i, m, n, err = 0, ampm = -1; + double jd; char *p, *q, in = '\0', sepc = '\0'; tss->year = tss->month = tss->day = 0; tss->hour = tss->minute = tss->second = 0; tss->fraction = 0; + if (jdconv) { + p = strchr(str, '.'); + if (p) { + q = strchr(str, '-'); + if (q == str) { + q = 0; + } + if (!q) { + q = strchr(str, '/'); + if (!q) { + q = strchr(str, ':'); + } + } + if (!q || q > p) { + /* julian day format */ + p = 0; + jd = ln_strtod(str, &p); + if (p && p > str) { + DATE_STRUCT ds; + TIME_STRUCT ts; + + convJD2YMD(jd, &ds); + convJD2HMS(jd, &ts, &n); + tss->year = ds.year; + tss->month = ds.month; + tss->day = ds.day; + tss->hour = ts.hour; + tss->minute = ts.minute; + tss->second = ts.second; + tss->fraction = n; + return 0; + } + } + } + } p = str; while (*p && !ISDIGIT(*p)) { ++p; @@ -2945,7 +3410,7 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) ++m; } buf[m] = '\0'; - tss->fraction = strtol(buf, NULL, 0); + tss->fraction = strtol(buf, NULL, 10); } m = 7; goto done; @@ -3024,9 +3489,8 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) if (*q == ' ') { if ((m & 1) == 0) { char *e = NULL; - int dummy; - dummy = strtol(q + 1, &e, 10); + (void) strtol(q + 1, &e, 10); if (e && *e == '-') { goto skip; } @@ -3090,7 +3554,7 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) } p = q; q = NULL; - nn = strtol(p, &q, 0); + nn = strtol(p, &q, 10); tss->minute += nn * sign; if ((SQLSMALLINT) tss->minute < 0) { tss->hour -= 1; @@ -3326,6 +3790,7 @@ blob_export(sqlite3_context *ctx, int nargs, sqlite3_value **args) #endif if (f) { nn = fwrite(p, 1, n, f); + fclose(f); if (nn != n) { sqlite3_result_error(ctx, "write error", -1); } else { @@ -3450,7 +3915,11 @@ dbopen(DBC *d, char *name, int isu, char *dsn, char *sflag, d->dbname); fflush(d->trace); } +#if defined(HAVE_SQLITE3CLOSEV2) && (HAVE_SQLITE3CLOSEV2) + sqlite3_close_v2(d->sqlite); +#else sqlite3_close(d->sqlite); +#endif d->sqlite = NULL; } #if defined(HAVE_SQLITE3VFS) && (HAVE_SQLITE3VFS) @@ -3459,7 +3928,15 @@ dbopen(DBC *d, char *name, int isu, char *dsn, char *sflag, } #if defined(_WIN32) || defined(_WIN64) if (!isu) { - uname = wmb_to_utf(name, -1); + char expname[MAX_PATH]; + + expname[0] = '\0'; + rc = ExpandEnvironmentStrings(name, expname, sizeof (expname)); + if (rc <= sizeof (expname)) { + uname = wmb_to_utf(expname, rc - 1); + } else { + uname = wmb_to_utf(name, -1); + } if (!uname) { rc = SQLITE_NOMEM; setstatd(d, rc, "out of memory", (*d->ov3) ? "HY000" : "S1000"); @@ -3554,6 +4031,10 @@ connfail: busyto = 1000000; } d->timeout = busyto; + freep(&d->dbname); + d->dbname = xstrdup(name); + freep(&d->dsn); + d->dsn = xstrdup(dsn); if ((rc = setsqliteopts(d->sqlite, d)) != SQLITE_OK) { if (d->trace) { fprintf(d->trace, "-- sqlite3_close: '%s'\n", @@ -3579,10 +4060,6 @@ connfail: sprintf(jourp, "PRAGMA journal_mode = %16.16s;", jmode); sqlite3_exec(d->sqlite, jourp, NULL, NULL, NULL); } - freep(&d->dbname); - d->dbname = xstrdup(name); - freep(&d->dsn); - d->dsn = xstrdup(dsn); if (d->trace) { fprintf(d->trace, "-- sqlite3_open: '%s'\n", d->dbname); fflush(d->trace); @@ -3756,16 +4233,18 @@ static void s3stmt_addmeta(sqlite3_stmt *s3stmt, int col, DBC *d, COL *ci) { int nn = 0, pk = 0, ai = 0; - const char *dn, *tn, *cn, *dummy1, *dummy2; + const char *dn, *tn, *cn, *dummy[4]; dn = sqlite3_column_database_name(s3stmt, col); tn = sqlite3_column_table_name(s3stmt, col); cn = sqlite3_column_origin_name(s3stmt, col); + dummy[0] = dummy[1] = 0; sqlite3_table_column_metadata(d->sqlite, dn, tn, cn, - &dummy1, &dummy2, + dummy, dummy + 1, &nn, &pk, &ai); ci->autoinc = ai ? SQL_TRUE: SQL_FALSE; ci->notnull = nn ? SQL_NO_NULLS : SQL_NULLABLE; + ci->ispk = pk ? 1 : 0; if (d->trace) { fprintf(d->trace, "-- column %d %s\n", col + 1, nn ? "notnull" : "nullable"); @@ -3774,6 +4253,18 @@ s3stmt_addmeta(sqlite3_stmt *s3stmt, int col, DBC *d, COL *ci) } fflush(d->trace); } + ci->isrowid = 0; + if (ci->ispk) { + nn = pk = ai = 0; + dummy[2] = dummy[3] = 0; + + sqlite3_table_column_metadata(d->sqlite, dn, tn, "rowid", + dummy + 2, dummy + 3, + &nn, &pk, &ai); + if (pk && dummy[0] && dummy[0] == dummy[2]) { + ci->isrowid = 1; + } + } } #endif @@ -3905,6 +4396,8 @@ s3stmt_step(STMT *s) #else dyncols[i].autoinc = SQL_FALSE; dyncols[i].notnull = SQL_NULLABLE; + dyncols[i].ispk = -1; + dyncols[i].isrowid = -1; #endif dyncols[i].typename = xstrdup(typename); } @@ -3955,6 +4448,30 @@ s3stmt_step(STMT *s) *qp++ = '\''; *qp = '\0'; } +#ifdef _MSC_VER + } else if (coltype == SQLITE_FLOAT) { + static struct lconv *lc = 0; + double d = sqlite3_column_double(s->s3stmt, i); + char *p, buffer[128]; + + /* + * This avoids floating point rounding + * and formatting problems of some SQLite + * versions in conjunction with MSVC 2010. + */ + snprintf(buffer, sizeof (buffer), "%.15g", d); + if (!lc) { + lc = localeconv(); + } + if (lc && lc->decimal_point && lc->decimal_point[0] && + lc->decimal_point[0] != '.') { + p = strchr(buffer, lc->decimal_point[0]); + if (p) { + *p = '.'; + } + } + rowd[i + ncols] = xstrdup(buffer); +#endif } else if (coltype != SQLITE_NULL) { value = sqlite3_column_text(s->s3stmt, i); rowd[i + ncols] = xstrdup((char *) value); @@ -4133,21 +4650,6 @@ s3stmt_start(STMT *s) return SQL_SUCCESS; } -/** - * Function not implemented. - */ - -SQLRETURN SQL_API -SQLBulkOperations(SQLHSTMT stmt, SQLSMALLINT oper) -{ - SQLRETURN ret; - - HSTMT_LOCK(stmt); - ret = drvunimplstmt(stmt); - HSTMT_UNLOCK(stmt); - return ret; -} - #ifndef WINTERFACE /** * Function not implemented. @@ -4278,7 +4780,7 @@ seqerr: setstat(s, -1, "sequence error", "HY010"); return SQL_ERROR; } - for (i = 0; i < s->nparams; i++) { + for (i = (s->pdcount < 0) ? 0 : s->pdcount; i < s->nparams; i++) { p = &s->bindparms[i]; if (p->need > 0) { int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); @@ -4302,17 +4804,17 @@ seqerr: #ifdef SQL_BIT case SQL_C_BIT: #endif - size = sizeof (char); + size = sizeof (SQLCHAR); break; case SQL_C_SHORT: case SQL_C_USHORT: case SQL_C_SSHORT: - size = sizeof (short); + size = sizeof (SQLSMALLINT); break; case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_SLONG: - size = sizeof (long); + size = sizeof (SQLINTEGER); break; #ifdef SQL_BIGINT case SQL_C_UBIGINT: @@ -4438,7 +4940,10 @@ seqerr: p->need = (type == SQL_C_CHAR) ? -1 : 0; #endif #if defined(_WIN32) || defined(_WIN64) - if (type == SQL_C_CHAR && *s->oemcp) { + if (type == SQL_C_CHAR && *s->oemcp && + !(p->stype == SQL_BINARY || + p->stype == SQL_VARBINARY || + p->stype == SQL_LONGVARBINARY)) { char *dp = wmb_to_utf(p->param, p->len); if (!dp) { @@ -4545,6 +5050,12 @@ setupparam(STMT *s, char *sql, int pnum) p->s3size = 0; return SQL_SUCCESS; } + if (type == SQL_C_CHAR && + (p->stype == SQL_BINARY || + p->stype == SQL_VARBINARY || + p->stype == SQL_LONGVARBINARY)) { + type = SQL_C_BINARY; + } switch (type) { case SQL_C_BINARY: p->s3type = SQLITE_BLOB; @@ -4555,6 +5066,8 @@ setupparam(STMT *s, char *sql, int pnum) } if (!p->lenp) { len = p->len; + } else if (*p->lenp == SQL_DATA_AT_EXEC) { + len = p->len; } else { len = *p->lenp; if (len <= SQL_LEN_DATA_AT_EXEC_OFFSET) { @@ -4577,10 +5090,10 @@ setupparam(STMT *s, char *sql, int pnum) p->s3type = SQLITE_TEXT; p->s3size = -1; p->s3val = p->param; - if (!p->parbuf && p->lenp) { + if (!p->parbuf) { #ifdef WCHARSUPPORT if (type == SQL_C_WCHAR) { - if (*p->lenp == SQL_NTS) { + if (!p->lenp || *p->lenp == SQL_NTS) { p->max = uc_strlen(p->param) * sizeof (SQLWCHAR); } else if (*p->lenp >= 0) { p->max = *p->lenp; @@ -4588,7 +5101,7 @@ setupparam(STMT *s, char *sql, int pnum) } else #endif if (type == SQL_C_CHAR) { - if (*p->lenp == SQL_NTS) { + if (!p->lenp || *p->lenp == SQL_NTS) { p->len = p->max = strlen(p->param); #if defined(_WIN32) || defined(_WIN64) needalloc = 1; @@ -4658,43 +5171,39 @@ setupparam(STMT *s, char *sql, int pnum) } break; case SQL_C_UTINYINT: - p->s3type = SQLITE_INTEGER; - p->s3size = sizeof (int); - p->s3ival = *((unsigned char *) p->param); - break; case SQL_C_TINYINT: case SQL_C_STINYINT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = *((char *) p->param); + p->s3ival = *((SQLCHAR *) p->param); break; case SQL_C_USHORT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = *((unsigned short *) p->param); + p->s3ival = *((SQLUSMALLINT *) p->param); break; case SQL_C_SHORT: case SQL_C_SSHORT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = *((short *) p->param); + p->s3ival = *((SQLSMALLINT *) p->param); break; case SQL_C_ULONG: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = *((unsigned int *) p->param); + p->s3ival = *((SQLUINTEGER *) p->param); break; case SQL_C_LONG: case SQL_C_SLONG: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = *((int *) p->param); + p->s3ival = *((SQLINTEGER *) p->param); break; #ifdef SQL_BIT case SQL_C_BIT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); - p->s3ival = (*((unsigned char *) p->param)) ? 1 : 0; + p->s3ival = (*((SQLCHAR *) p->param)) ? 1 : 0; break; #endif #ifdef SQL_BIGINT @@ -4723,6 +5232,25 @@ setupparam(STMT *s, char *sql, int pnum) case SQL_C_TYPE_DATE: #endif case SQL_C_DATE: + if (*s->jdconv) { + int a, b, x1, x2, y, m, d; + + p->s3type = SQLITE_FLOAT; + p->s3size = sizeof (double); + y = ((DATE_STRUCT *) p->param)->year; + m = ((DATE_STRUCT *) p->param)->month; + d = ((DATE_STRUCT *) p->param)->day; + if (m <= 2) { + y--; + m += 12; + } + a = y / 100; + b = 2 - a + (a / 4); + x1 = 36525 * (y + 4716) / 100; + x2 = 306001 * (m + 1) / 10000; + p->s3dval = x1 + x2 + d + b - 1524.5; + break; + } sprintf(p->strbuf, "%04d-%02d-%02d", ((DATE_STRUCT *) p->param)->year, ((DATE_STRUCT *) p->param)->month, @@ -4735,6 +5263,15 @@ setupparam(STMT *s, char *sql, int pnum) case SQL_C_TYPE_TIME: #endif case SQL_C_TIME: + if (*s->jdconv) { + p->s3type = SQLITE_FLOAT; + p->s3size = sizeof (double); + p->s3dval = 2451544.5 + + (((TIME_STRUCT *) p->param)->hour * 3600000.0 + + ((TIME_STRUCT *) p->param)->minute * 60000.0 + + ((TIME_STRUCT *) p->param)->second * 1000.0) / 86400000.0; + break; + } sprintf(p->strbuf, "%02d:%02d:%02d", ((TIME_STRUCT *) p->param)->hour, ((TIME_STRUCT *) p->param)->minute, @@ -4747,6 +5284,30 @@ setupparam(STMT *s, char *sql, int pnum) case SQL_C_TYPE_TIMESTAMP: #endif case SQL_C_TIMESTAMP: + if (*s->jdconv) { + int a, b, x1, x2, y, m, d; + + p->s3type = SQLITE_FLOAT; + p->s3size = sizeof (double); + y = ((TIMESTAMP_STRUCT *) p->param)->year; + m = ((TIMESTAMP_STRUCT *) p->param)->month; + d = ((TIMESTAMP_STRUCT *) p->param)->day; + if (m <= 2) { + y--; + m += 12; + } + a = y / 100; + b = 2 - a + (a / 4); + x1 = 36525 * (y + 4716) / 100; + x2 = 306001 * (m + 1) / 10000; + p->s3dval = x1 + x2 + d + b - 1524.5 + + (((TIMESTAMP_STRUCT *) p->param)->hour * 3600000.0 + + ((TIMESTAMP_STRUCT *) p->param)->minute * 60000.0 + + ((TIMESTAMP_STRUCT *) p->param)->second * 1000.0 + + ((TIMESTAMP_STRUCT *) p->param)->fraction / 1.0E6) + / 86400000.0; + break; + } len = (int) ((TIMESTAMP_STRUCT *) p->param)->fraction; len /= 1000000; len = len % 1000; @@ -4860,17 +5421,17 @@ outofmem: #ifdef SQL_C_BIT case SQL_C_BIT: #endif - buflen = sizeof (char); + buflen = sizeof (SQLCHAR); break; case SQL_C_SHORT: case SQL_C_USHORT: case SQL_C_SSHORT: - buflen = sizeof (short); + buflen = sizeof (SQLSMALLINT); break; case SQL_C_SLONG: case SQL_C_ULONG: case SQL_C_LONG: - buflen = sizeof (long); + buflen = sizeof (SQLINTEGER); break; case SQL_C_FLOAT: buflen = sizeof (float); @@ -5027,14 +5588,18 @@ static SQLRETURN setupparbuf(STMT *s, BINDPARM *p) { if (!p->parbuf) { - p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp); + if (*p->lenp == SQL_DATA_AT_EXEC) { + p->len = p->max; + } else { + p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp); + } if (p->len < 0 && p->len != SQL_NTS && p->len != SQL_NULL_DATA) { setstat(s, -1, "invalid length", "HY009"); return SQL_ERROR; } if (p->len >= 0) { - p->parbuf = xmalloc(p->len + 1); + p->parbuf = xmalloc(p->len + 2); if (!p->parbuf) { return nomem(s); } @@ -5060,6 +5625,7 @@ SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind) int i; SQLPOINTER dummy; SQLRETURN ret; + BINDPARM *p; HSTMT_LOCK(stmt); if (stmt == SQL_NULL_HSTMT) { @@ -5069,12 +5635,23 @@ SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind) if (!pind) { pind = &dummy; } - for (i = 0; i < s->nparams; i++) { - BINDPARM *p = &s->bindparms[i]; + if (s->pdcount < s->nparams) { + s->pdcount++; + } + for (i = 0; i < s->pdcount; i++) { + p = &s->bindparms[i]; + if (p->need > 0) { + int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); + p->need = (type == SQL_C_CHAR || type == SQL_C_WCHAR) ? -1 : 0; + } + } + for (; i < s->nparams; i++) { + p = &s->bindparms[i]; if (p->need > 0) { *pind = (SQLPOINTER) p->param0; ret = setupparbuf(s, p); + s->pdcount = i; goto done; } } @@ -5336,7 +5913,7 @@ noconn: mkbindcols(s, s->ncols); s->nowchar[1] = 1; s->nrows = 0; - s->rowp = -1; + s->rowp = s->rowprs = -1; s->isselect = -1; if (nret) { *nret = s->ncols; @@ -5583,7 +6160,7 @@ doit: sqlite3_free(errp); errp = NULL; } - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_SUCCESS; } @@ -8116,7 +8693,7 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, { DBC *d = NULL; STMT *s = NULL; - int len, naterr; + int len, naterr, strbuf = 1; char *logmsg, *sqlst, *clrmsg = NULL; SQLRETURN ret = SQL_ERROR; @@ -8149,7 +8726,18 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, return SQL_INVALID_HANDLE; } if (buflen < 0) { - goto done; + switch (buflen) { + case SQL_IS_POINTER: + case SQL_IS_UINTEGER: + case SQL_IS_INTEGER: + case SQL_IS_USMALLINT: + case SQL_IS_SMALLINT: + strbuf = 0; + break; + default: + ret = SQL_ERROR; + goto done; + } } if (recno > 1) { ret = SQL_NO_DATA; @@ -8180,7 +8768,9 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, logmsg = sqlst; break; case SQL_DIAG_MESSAGE_TEXT: - clrmsg = logmsg; + if (info) { + clrmsg = logmsg; + } break; case SQL_DIAG_NUMBER: naterr = 1; @@ -8196,6 +8786,27 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, } ret = SQL_SUCCESS; goto done; + case SQL_DIAG_DYNAMIC_FUNCTION: + logmsg = ""; + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = (s->isselect == 1 || s->isselect == -1) ? s->nrows : 0; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; + case SQL_DIAG_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = s->isselect ? 0 : s->nrows; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; default: goto done; } @@ -8210,16 +8821,18 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, if (stringlen) { *stringlen = len; } - if (len >= buflen) { - if (info && buflen > 0) { - if (stringlen) { - *stringlen = buflen - 1; + if (strbuf) { + if (len >= buflen) { + if (info && buflen > 0) { + if (stringlen) { + *stringlen = buflen - 1; + } + strncpy((char *) info, logmsg, buflen); + ((char *) info)[buflen - 1] = '\0'; } - strncpy((char *) info, logmsg, buflen); - ((char *) info)[buflen - 1] = '\0'; + } else if (info) { + strcpy((char *) info, logmsg); } - } else if (info) { - strcpy((char *) info, logmsg); } if (clrmsg) { *clrmsg = '\0'; @@ -8290,6 +8903,7 @@ SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, case SQL_DIAG_SERVER_NAME: case SQL_DIAG_SQLSTATE: case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: if (len > 0) { SQLWCHAR *m = NULL; @@ -8324,6 +8938,7 @@ SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, case SQL_DIAG_SERVER_NAME: case SQL_DIAG_SQLSTATE: case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: len *= sizeof (SQLWCHAR); break; } @@ -8406,9 +9021,12 @@ drvgetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, case SQL_ATTR_USE_BOOKMARKS: { STMT *s = (STMT *) stmt; - *(SQLUINTEGER *) val = s->bkmrk ? SQL_UB_ON : SQL_UB_OFF; + *(SQLUINTEGER *) val = s->bkmrk; return SQL_SUCCESS; } + case SQL_ATTR_FETCH_BOOKMARK_PTR: + *(SQLPOINTER *) val = s->bkmrkptr; + return SQL_SUCCESS; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: *((SQLULEN **) val) = s->parm_bind_offs; return SQL_SUCCESS; @@ -8622,10 +9240,22 @@ drvsetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, return SQL_SUCCESS; case SQL_ATTR_USE_BOOKMARKS: if (val != (SQLPOINTER) SQL_UB_OFF && - val != (SQLPOINTER) SQL_UB_ON) { + val != (SQLPOINTER) SQL_UB_ON && + val != (SQLPOINTER) SQL_UB_VARIABLE) { goto e01s02; } - s->bkmrk = val == (SQLPOINTER) SQL_UB_ON; + if (*s->ov3 && val == (SQLPOINTER) SQL_UB_VARIABLE) { + s->bkmrk = SQL_UB_VARIABLE; + return SQL_SUCCESS; + } + if (val == (SQLPOINTER) SQL_UB_VARIABLE) { + s->bkmrk = SQL_UB_ON; + goto e01s02; + } + s->bkmrk = (val == (SQLPOINTER) SQL_UB_ON) ? SQL_UB_ON : SQL_UB_OFF; + return SQL_SUCCESS; + case SQL_ATTR_FETCH_BOOKMARK_PTR: + s->bkmrkptr = (SQLINTEGER *) val; return SQL_SUCCESS; case SQL_ATTR_MAX_ROWS: s->max_rows = uval; @@ -8905,6 +9535,477 @@ SQLSetStmtOptionW(SQLHSTMT stmt, SQLUSMALLINT opt, #endif /** + * Check for unbound result columns. + * @param s statement handle + * @result ODBC error code + */ + +static SQLRETURN +chkunbound(STMT *s) +{ + int i; + + if (!s->bindcols || s->nbindcols < s->ncols) { +unbound: + setstat(s, -1, "unbound columns", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + for (i = 0; i < s->ncols; i++) { + BINDCOL *b = &s->bindcols[i]; + + if (b->type == SQL_UNKNOWN_TYPE || !b->valp) { + goto unbound; + } + } + return SQL_SUCCESS; +} + +/** + * Internal handler to setup parameters for positional updates + * from bound user buffers. + * @param s statement handle + * @param stmt SQLite3 statement pointer + * @param i result set column index + * @param si SQLite3 parameter index + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposbind(STMT *s, sqlite3_stmt *stmt, int i, int si, int rsi) +{ + DBC *d = (DBC *) s->dbc; + SQLPOINTER dp = 0; + SQLLEN *lp = 0; + BINDCOL *b = &s->bindcols[i]; + COL *c = &s->cols[i]; + char strbuf[128], *cp; + + if (b->valp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi); + } else { + dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi); + } + if (s->bind_offs) { + dp = (SQLPOINTER) ((char *) dp + *s->bind_offs); + } + } + if (b->lenp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi); + } else { + lp = b->lenp + rsi; + } + if (s->bind_offs) { + lp = (SQLLEN *) ((char *) lp + *s->bind_offs); + } + } + if (!dp || !lp) { + setstat(s, -1, "unbound column in positional update", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (*lp == SQL_NULL_DATA) { + sqlite3_bind_null(stmt, si); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: NULL\n", si); + fflush(d->trace); + } + return SQL_SUCCESS; + } + switch (b->type) { + case SQL_C_UTINYINT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + sqlite3_bind_int(stmt, si, *(SQLCHAR *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, *(SQLCHAR *) dp); + fflush(d->trace); + } + break; +#ifdef SQL_BIT + case SQL_C_BIT: + sqlite3_bind_int(stmt, si, (*(SQLCHAR *) dp) ? 1 : 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + (*(SQLCHAR *) dp) ? 1 : 0); + fflush(d->trace); + } + break; +#endif + case SQL_C_USHORT: + sqlite3_bind_int(stmt, si, *(SQLUSMALLINT *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + *(SQLUSMALLINT *) dp); + fflush(d->trace); + } + break; + case SQL_C_SHORT: + case SQL_C_SSHORT: + sqlite3_bind_int(stmt, si, *(SQLSMALLINT *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + *(SQLSMALLINT *) dp); + fflush(d->trace); + } + break; + case SQL_C_ULONG: + sqlite3_bind_int(stmt, si, *(SQLUINTEGER *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %ld\n", si, + (long) *(SQLUINTEGER *) dp); + fflush(d->trace); + } + break; + case SQL_C_LONG: + case SQL_C_SLONG: + sqlite3_bind_int(stmt, si, *(SQLINTEGER *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %ld\n", si, + (long) *(SQLINTEGER *) dp); + fflush(d->trace); + } + break; +#ifdef SQL_BIGINT + case SQL_C_UBIGINT: + case SQL_C_SBIGINT: + sqlite3_bind_int64(stmt, si, *(SQLBIGINT *) dp); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter %d: %I64d\n", +#else + "-- parameter %d: %lld\n", +#endif + si, (sqlite_int64) *(SQLBIGINT *) dp); + fflush(d->trace); + } + break; +#endif + case SQL_C_FLOAT: + sqlite3_bind_double(stmt, si, *(float *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, + *(float *) dp); + fflush(d->trace); + } + break; + case SQL_C_DOUBLE: + sqlite3_bind_double(stmt, si, *(double *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, + *(double *) dp); + fflush(d->trace); + } + break; + case SQL_C_BINARY: + sqlite3_bind_blob(stmt, si, (char *) dp, *lp, SQLITE_STATIC); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: [BLOB]\n", si); + fflush(d->trace); + } + break; +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: + cp = uc_to_utf((SQLWCHAR *) dp, *lp); + if (!cp) { + return nomem(s); + } + sqlite3_bind_text(stmt, si, cp, -1, SQLITE_TRANSIENT); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, cp); + fflush(d->trace); + } + uc_free(cp); + break; +#endif + case SQL_C_CHAR: +#if defined(_WIN32) || defined(_WIN64) + if (*s->oemcp) { + cp = wmb_to_utf((char *) dp, *lp); + if (!cp) { + return nomem(s); + } + sqlite3_bind_text(stmt, si, cp, -1, SQLITE_TRANSIENT); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, cp); + fflush(d->trace); + } + uc_free(cp); + } else +#endif + { + if (*lp == SQL_NTS) { + sqlite3_bind_text(stmt, si, (char *) dp, -1, + SQLITE_STATIC); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, + (char *) dp); + fflush(d->trace); + } + } else { + sqlite3_bind_text(stmt, si, (char *) dp, *lp, + SQLITE_STATIC); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%*s'\n", si, + (int) *lp, (char *) dp); + fflush(d->trace); + } + } + } + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + if (*s->jdconv) { + int a, b, x1, x2, y, m, dd; + double v; + + y = ((DATE_STRUCT *) dp)->year; + m = ((DATE_STRUCT *) dp)->month; + dd = ((DATE_STRUCT *) dp)->day; + if (m <= 2) { + y--; + m += 12; + } + a = y / 100; + b = 2 - a + (a / 4); + x1 = 36525 * (y + 4716) / 100; + x2 = 306001 * (m + 1) / 10000; + v = x1 + x2 + dd + b - 1524.5; + sqlite3_bind_double(stmt, si, v); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, v); + fflush(d->trace); + } + } else { + sprintf(strbuf, "%04d-%02d-%02d", + ((DATE_STRUCT *) dp)->year, + ((DATE_STRUCT *) dp)->month, + ((DATE_STRUCT *) dp)->day); + sqlite3_bind_text(stmt, si, strbuf, -1, SQLITE_TRANSIENT); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + } + break; +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + if (*s->jdconv) { + double v; + + v = 2451544.5 + + (((TIME_STRUCT *) dp)->hour * 3600000.0 + + ((TIME_STRUCT *) dp)->minute * 60000.0 + + ((TIME_STRUCT *) dp)->second * 1000.0) / 86400000.0; + sqlite3_bind_double(stmt, si, v); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, v); + fflush(d->trace); + } + } else { + sprintf(strbuf, "%02d:%02d:%02d", + ((TIME_STRUCT *) dp)->hour, + ((TIME_STRUCT *) dp)->minute, + ((TIME_STRUCT *) dp)->second); + sqlite3_bind_text(stmt, si, strbuf, -1, SQLITE_TRANSIENT); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + } + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + if (*s->jdconv) { + int a, b, x1, x2, y, m, dd; + double v; + + y = ((TIMESTAMP_STRUCT *) dp)->year; + m = ((TIMESTAMP_STRUCT *) dp)->month; + dd = ((TIMESTAMP_STRUCT *) dp)->day; + if (m <= 2) { + y--; + m += 12; + } + a = y / 100; + b = 2 - a + (a / 4); + x1 = 36525 * (y + 4716) / 100; + x2 = 306001 * (m + 1) / 10000; + v = x1 + x2 + dd + b - 1524.5 + + (((TIMESTAMP_STRUCT *) dp)->hour * 3600000.0 + + ((TIMESTAMP_STRUCT *) dp)->minute * 60000.0 + + ((TIMESTAMP_STRUCT *) dp)->second * 1000.0 + + ((TIMESTAMP_STRUCT *) dp)->fraction / 1.0E6) + / 86400000.0; + sqlite3_bind_double(stmt, si, v); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, v); + fflush(d->trace); + } + } else { + int frac; + + frac = (int) ((TIMESTAMP_STRUCT *) dp)->fraction; + frac /= 1000000; + frac = frac % 1000; + if (frac < 0) { + frac = 0; + } + if (c->prec && c->prec <= 16) { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:00.000", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute); + } else if (c->prec && c->prec <= 19) { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:%02d.000", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute, + ((TIMESTAMP_STRUCT *) dp)->second); + } else { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute, + ((TIMESTAMP_STRUCT *) dp)->second, + frac); + } + sqlite3_bind_text(stmt, si, strbuf, -1, SQLITE_TRANSIENT); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + } + break; + default: + setstat(s, -1, "unsupported column type in positional update", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +/** + * Internal handler to setup parameters for positional updates + * from driver side result set. + * @param s statement handle + * @param stmt SQLite3 statement pointer + * @param i result set column index + * @param si SQLite3 parameter index + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposibind(STMT *s, sqlite3_stmt *stmt, int i, int si, int rsi) +{ + DBC *d = (DBC *) s->dbc; + char **data; + int pos; + + pos = s->rowprs; + if (pos < 0) { + setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107"); + return SQL_ERROR; + } + pos += rsi; + data = s->rows + s->ncols + (pos * s->ncols) + i; + if (*data == NULL) { + sqlite3_bind_null(stmt, si); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: NULL\n", si); + fflush(d->trace); + } + } else { + sqlite3_bind_text(stmt, si, *data, -1, SQLITE_STATIC); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, *data); + fflush(d->trace); + } + } + return SQL_SUCCESS; +} + +/** + * Internal handler to refresh user buffers from driver side result set. + * @param s statement handle + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposrefr(STMT *s, int rsi) +{ + int i, withinfo = 0; + SQLRETURN ret = SQL_SUCCESS; + + for (i = 0; s->bindcols && i < s->ncols; i++) { + BINDCOL *b = &s->bindcols[i]; + SQLPOINTER dp = 0; + SQLLEN *lp = 0; + + b->offs = 0; + if (b->valp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi); + } else { + dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi); + } + if (s->bind_offs) { + dp = (SQLPOINTER) ((char *) dp + *s->bind_offs); + } + } + if (b->lenp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi); + } else { + lp = b->lenp + rsi; + } + if (s->bind_offs) { + lp = (SQLLEN *) ((char *) lp + *s->bind_offs); + } + } + if (dp || lp) { + int rowp = s->rowp; + + s->rowp = s->rowprs + rsi; + ret = getrowdata(s, (SQLUSMALLINT) i, b->type, dp, + b->max, lp, 0); + s->rowp = rowp; + if (!SQL_SUCCEEDED(ret)) { + s->row_status0[rsi] = SQL_ROW_ERROR; + break; + } + if (ret != SQL_SUCCESS) { + withinfo = 1; +#ifdef SQL_ROW_SUCCESS_WITH_INFO + s->row_status0[rsi] = SQL_ROW_SUCCESS_WITH_INFO; +#endif + } + } + } + if (SQL_SUCCEEDED(ret)) { + ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS; + } + return ret; +} + +/** * Internal set position on result in HSTMT. * @param stmt statement handle * @param row row to be positioned @@ -8917,17 +10018,335 @@ static SQLRETURN drvsetpos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock) { STMT *s = (STMT *) stmt; - int rowp; + DBC *d = (DBC *) s->dbc; + int rowp, i, k, rc, nretry = 0; + dstr *sql = 0; + const char *endp; + sqlite3_stmt *s3stmt = NULL; + SQLRETURN ret; - if (op != SQL_POSITION) { - return drvunimplstmt(stmt); + if (lock != SQL_LOCK_NO_CHANGE) { + setstat(s, -1, "unsupported locking mode", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; } - rowp = s->rowp + row - 1; - if (!s->rows || row <= 0 || rowp < -1 || rowp >= s->nrows) { - setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107"); + if (s->isselect != 1 || s->curtype != SQL_CURSOR_STATIC) { + setstat(s, -1, "incompatible statement", + (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } - s->rowp = rowp; + if (op == SQL_ADD) { + if (s->one_tbl <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (row == 0 || row > s->rowset_size + 1) { + goto rowoor; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "INSERT INTO "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? "," : "("); + sql = dsappendq(sql, s->dyncols[i].column); + } + sql = dsappend(sql, ") VALUES "); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ",?" : "(?"); + } + sql = dsappend(sql, ")"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { +istmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s3stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + goto istmterr; + } + sqlite3_finalize(s3stmt); + if (sqlite3_changes(d->sqlite) > 0 && row <= s->rowset_size) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_ADDED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_ADDED; + } + } + return SQL_SUCCESS; + } else if (op == SQL_UPDATE || op == SQL_DELETE) { + if (s->one_tbl <= 0 || s->has_pk <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (row == 0) { + ret = SQL_SUCCESS; + for (i = 1; i <= s->rowset_size; i++) { + ret = drvsetpos(stmt, i, op, lock); + if (!SQL_SUCCEEDED(ret)) { + break; + } + } + return ret; + } + if (row > s->rowset_size) { + goto rowoor; + } + } + if (op != SQL_POSITION && op != SQL_REFRESH && + op != SQL_DELETE && op != SQL_UPDATE) { + return drvunimplstmt(stmt); + } + if (op == SQL_POSITION) { + rowp = s->rowp + row - 1; + if (!s->rows || row == 0 || rowp < -1 || rowp >= s->nrows) { +rowoor: + setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107"); + return SQL_ERROR; + } + s->rowp = rowp; + } else if (op == SQL_REFRESH) { + if (row > s->rowset_size) { + goto rowoor; + } + if (row == 0) { + ret = SQL_SUCCESS; + for (i = 0; i < s->rowset_size; i++) { + ret = setposrefr(s, i); + if (!SQL_SUCCEEDED(ret)) { + break; + } + } + return ret; + } + return setposrefr(s, row - 1); + } else if (op == SQL_DELETE) { + sql = dsappend(sql, "DELETE FROM "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = k = 0; i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + sql = dsappend(sql, (k > 0) ? " AND " : " WHERE "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { +dstmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + ret = setposibind(s, s3stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + goto dstmterr; + } + sqlite3_finalize(s3stmt); + if (sqlite3_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_DELETED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_DELETED; + } + } + return SQL_SUCCESS; + } else if (op == SQL_UPDATE) { + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "UPDATE "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ", " : " SET "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + } + for (i = k = 0; i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + sql = dsappend(sql, (k > 0) ? " AND " : " WHERE "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { +ustmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s3stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + for (i = 0; s->bindcols && i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + ret = setposibind(s, s3stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + goto ustmterr; + } + sqlite3_finalize(s3stmt); + if (sqlite3_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_UPDATED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_UPDATED; + } + } + return SQL_SUCCESS; + } return SQL_SUCCESS; } @@ -8952,6 +10371,456 @@ SQLSetPos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock) } /** + * Internal perform bulk operation on HSTMT. + * @param stmt statement handle + * @param op operation to be performed + * @result ODBC error code + */ + +static SQLRETURN +drvbulkoperations(SQLHSTMT stmt, SQLSMALLINT op) +{ + STMT *s = (STMT *) stmt; + DBC *d = (DBC *) s->dbc; + int row, i, k, rc, nretry = 0; + dstr *sql = 0; + const char *endp; + sqlite3_stmt *s3stmt = NULL; + SQLRETURN ret; + + if (s->isselect != 1 || s->curtype != SQL_CURSOR_STATIC) { + setstat(s, -1, "incompatible statement", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (op == SQL_ADD) { + if (s->one_tbl <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "INSERT INTO "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? "," : "("); + sql = dsappendq(sql, s->dyncols[i].column); + } + sql = dsappend(sql, ") VALUES "); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ",?" : "(?"); + } + sql = dsappend(sql, ")"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s3stmt, i, k, row); + if (ret != SQL_SUCCESS) { +istmterr: + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + ret = SQL_ERROR; + goto istmterr; + } + if (sqlite3_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ADDED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ADDED; + } + } + if (s->bkmrk == SQL_UB_VARIABLE && + s->bkmrkcol.type == SQL_C_VARBOOKMARK && + s->bkmrkcol.valp) { + SQLPOINTER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + *(sqlite_int64 *) val = sqlite3_last_insert_rowid(d->sqlite); + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + *ival = sizeof (sqlite_int64); + } + } + dbtraceapi(d, "sqlite3_reset", NULL); + sqlite3_reset(s3stmt); + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return SQL_SUCCESS; + } else if (op == SQL_DELETE_BY_BOOKMARK) { + if (s->has_rowid < 0 || + s->bkmrk != SQL_UB_VARIABLE || + s->bkmrkcol.type != SQL_C_VARBOOKMARK || + !s->bkmrkcol.valp) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + sql = dsappend(sql, "DELETE FROM "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + sql = dsappend(sql, " WHERE "); + sql = dsappendq(sql, s->dyncols[0].column); + sql = dsappend(sql, " = ?"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + SQLPOINTER *val; + sqlite_int64 rowid; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + if (*ival != sizeof (sqlite_int64)) { + continue; + } + } + rowid = *(sqlite_int64 *) val; + sqlite3_bind_int64(s3stmt, 1, rowid); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter 1: %I64d\n", +#else + "-- parameter 1: %lld\n", +#endif + rowid); + fflush(d->trace); + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return SQL_ERROR; + } + if (sqlite3_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_DELETED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_DELETED; + } + } + dbtraceapi(d, "sqlite3_reset", NULL); + sqlite3_reset(s3stmt); + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return SQL_SUCCESS; + } else if (op == SQL_UPDATE_BY_BOOKMARK) { + if (s->has_rowid < 0 || + s->bkmrk != SQL_UB_VARIABLE || + s->bkmrkcol.type != SQL_C_VARBOOKMARK || + !s->bkmrkcol.valp) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "UPDATE "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0, k = 0; i < s->ncols; i++) { + if (i == s->has_rowid) { + continue; + } + sql = dsappend(sql, (k > 0) ? ", " : " SET "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + sql = dsappend(sql, " WHERE "); + sql = dsappendq(sql, s->dyncols[s->has_rowid].column); + sql = dsappend(sql, " = ?"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + dbtraceapi(d, "sqlite3_prepare_v2", dsval(sql)); +#else + dbtraceapi(d, "sqlite3_prepare", dsval(sql)); +#endif + do { + s3stmt = NULL; +#if defined(HAVE_SQLITE3PREPAREV2) && (HAVE_SQLITE3PREPAREV2) + rc = sqlite3_prepare_v2(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#else + rc = sqlite3_prepare(d->sqlite, dsval(sql), -1, + &s3stmt, &endp); +#endif + if (rc != SQLITE_OK) { + if (s3stmt) { + sqlite3_finalize(s3stmt); + s3stmt = NULL; + } + } + } while (rc == SQLITE_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + if (s3stmt) { + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + SQLPOINTER *val; + sqlite_int64 rowid; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + if (*ival != sizeof (sqlite_int64)) { + continue; + } + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + if (i == s->has_rowid) { + continue; + } + ret = setposbind(s, s3stmt, i, k, row); + if (ret != SQL_SUCCESS) { +ustmterr: + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return ret; + } + k++; + } + rowid = *(sqlite_int64 *) val; + sqlite3_bind_int64(s3stmt, k, rowid); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter %d: %I64d\n", +#else + "-- parameter %d: %lld\n", +#endif + k, rowid); + fflush(d->trace); + } + rc = sqlite3_step(s3stmt); + if (rc != SQLITE_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite3_errmsg(d->sqlite), rc); + ret = SQL_ERROR; + goto ustmterr; + } + if (sqlite3_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_UPDATED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_UPDATED; + } + } + dbtraceapi(d, "sqlite3_reset", NULL); + sqlite3_reset(s3stmt); + } + dbtraceapi(d, "sqlite3_finalize", NULL); + sqlite3_finalize(s3stmt); + return SQL_SUCCESS; + } + setstat(s, -1, "unsupported operation", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; +} + +/** + * Perform bulk operation on HSTMT. + * @param stmt statement handle + * @param oper operation to be performed + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLBulkOperations(SQLHSTMT stmt, SQLSMALLINT oper) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbulkoperations(stmt, oper); + HSTMT_UNLOCK(stmt); + return ret; +} + + +/** * Function not implemented. */ @@ -8992,7 +10861,7 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, char dummyc[16]; SQLSMALLINT dummy; #if defined(_WIN32) || defined(_WIN64) - char drvname[301]; + char pathbuf[301], *drvname; #else static char drvname[] = "sqlite3odbc.so"; #endif @@ -9081,7 +10950,16 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, break; case SQL_DRIVER_NAME: #if defined(_WIN32) || defined(_WIN64) - GetModuleFileName(hModule, drvname, sizeof (drvname)); + GetModuleFileName(hModule, pathbuf, sizeof (pathbuf)); + drvname = strrchr(pathbuf, '\\'); + if (drvname == NULL) { + drvname = strrchr(pathbuf, '/'); + } + if (drvname == NULL) { + drvname = pathbuf; + } else { + drvname++; + } #endif strmak(val, drvname, valMax, valLen); break; @@ -9301,10 +11179,13 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, *valLen = sizeof (SQLUINTEGER); break; case SQL_POSITIONED_STATEMENTS: - case SQL_LOCK_TYPES: *((SQLUINTEGER *) val) = 0; *valLen = sizeof (SQLUINTEGER); break; + case SQL_LOCK_TYPES: + *((SQLUINTEGER *) val) = SQL_LCK_NO_CHANGE; + *valLen = sizeof (SQLUINTEGER); + break; case SQL_BOOKMARK_PERSISTENCE: *((SQLUINTEGER *) val) = SQL_BP_SCROLL; *valLen = sizeof (SQLUINTEGER); @@ -9325,7 +11206,8 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, *valLen = sizeof (SQLUSMALLINT); break; case SQL_POS_OPERATIONS: - *((SQLUINTEGER *) val) = 0; + *((SQLUINTEGER *) val) = SQL_POS_POSITION | SQL_POS_UPDATE | + SQL_POS_DELETE | SQL_POS_ADD | SQL_POS_REFRESH; *valLen = sizeof (SQLUINTEGER); break; case SQL_ALTER_TABLE: @@ -9425,7 +11307,10 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, break; case SQL_STATIC_CURSOR_ATTRIBUTES1: *((SQLUINTEGER *) val) = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | - SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK; + SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK | SQL_CA1_POS_POSITION | + SQL_CA1_POS_DELETE | SQL_CA1_POS_UPDATE | SQL_CA1_POS_REFRESH | + SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_BULK_ADD | + SQL_CA1_BULK_UPDATE_BY_BOOKMARK | SQL_CA1_BULK_DELETE_BY_BOOKMARK; *valLen = sizeof (SQLUINTEGER); break; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: @@ -9567,7 +11452,7 @@ SQLGetInfoW(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, } } } else { - len = 0; + len *= sizeof (SQLWCHAR); } break; } @@ -9591,14 +11476,12 @@ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC dbc, SQLUSMALLINT func, SQLUSMALLINT *flags) { - DBC *d; int i; SQLUSMALLINT exists[100]; if (dbc == SQL_NULL_HDBC) { return SQL_INVALID_HANDLE; } - d = (DBC *) dbc; for (i = 0; i < array_size(exists); i++) { exists[i] = SQL_FALSE; } @@ -9624,6 +11507,7 @@ SQLGetFunctions(SQLHDBC dbc, SQLUSMALLINT func, exists[SQL_API_SQLSETPARAM] = SQL_TRUE; exists[SQL_API_SQLEXECDIRECT] = SQL_TRUE; exists[SQL_API_SQLTRANSACT] = SQL_TRUE; + exists[SQL_API_SQLBULKOPERATIONS] = SQL_TRUE; exists[SQL_API_SQLEXECUTE] = SQL_TRUE; exists[SQL_API_SQLBINDPARAMETER] = SQL_TRUE; exists[SQL_API_SQLGETTYPEINFO] = SQL_TRUE; @@ -10559,6 +12443,7 @@ drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, char *pwd, char loadext[SQL_MAX_MESSAGE_LENGTH]; char sflag[32], spflag[32], ntflag[32], nwflag[32], biflag[32]; char snflag[32], lnflag[32], ncflag[32], fkflag[32], jmode[32]; + char jdflag[32]; #if defined(_WIN32) || defined(_WIN64) char oemcp[32]; #endif @@ -10633,6 +12518,8 @@ drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, char *pwd, getdsnattr(buf, "loadext", loadext, sizeof (loadext)); jmode[0] = '\0'; getdsnattr(buf, "journalmode", jmode, sizeof (jmode)); + jdflag[0] = '\0'; + getdsnattr(buf, "jdconv", jdflag, sizeof (jdflag)); #if defined(_WIN32) || defined(_WIN64) oemcp[0] = '\0'; getdsnattr(buf, "oemcp", oemcp, sizeof (oemcp)); @@ -10668,6 +12555,8 @@ drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, char *pwd, loadext, sizeof (loadext), ODBC_INI); SQLGetPrivateProfileString(buf, "journalmode", "", jmode, sizeof (jmode), ODBC_INI); + SQLGetPrivateProfileString(buf, "jdconv", "", + jdflag, sizeof (jdflag), ODBC_INI); #if defined(_WIN32) || defined(_WIN64) SQLGetPrivateProfileString(buf, "oemcp", "1", oemcp, sizeof (oemcp), ODBC_INI); @@ -10690,6 +12579,7 @@ drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, char *pwd, d->longnames = getbool(lnflag); d->nocreat = getbool(ncflag); d->fksupport = getbool(fkflag); + d->jdconv = getbool(jdflag); #if defined(_WIN32) || defined(_WIN64) d->oemcp = getbool(oemcp); #else @@ -10798,6 +12688,7 @@ static SQLRETURN drvdisconnect(SQLHDBC dbc) { DBC *d; + int rc; if (dbc == SQL_NULL_HDBC) { return SQL_INVALID_HANDLE; @@ -10819,7 +12710,11 @@ drvdisconnect(SQLHDBC dbc) d->dbname); fflush(d->trace); } - sqlite3_close(d->sqlite); + rc = sqlite3_close(d->sqlite); + if (rc == SQLITE_BUSY) { + setstatd(d, -1, "unfinished statements", "25000"); + return SQL_ERROR; + } d->sqlite = NULL; } freep(&d->dbname); @@ -10868,12 +12763,13 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, DBC *d; int len; SQLRETURN ret; - char buf[SQL_MAX_MESSAGE_LENGTH * 2], dbname[SQL_MAX_MESSAGE_LENGTH / 4]; + char buf[SQL_MAX_MESSAGE_LENGTH * 6], dbname[SQL_MAX_MESSAGE_LENGTH]; char dsn[SQL_MAX_MESSAGE_LENGTH / 4], busy[SQL_MAX_MESSAGE_LENGTH / 4]; char tracef[SQL_MAX_MESSAGE_LENGTH], loadext[SQL_MAX_MESSAGE_LENGTH]; char pwd[SQL_MAX_MESSAGE_LENGTH]; char sflag[32], spflag[32], ntflag[32], snflag[32], lnflag[32]; char ncflag[32], nwflag[32], fkflag[32], jmode[32], biflag[32]; + char jdflag[32]; if (dbc == SQL_NULL_HDBC) { return SQL_INVALID_HANDLE; @@ -11017,6 +12913,14 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, biflag, sizeof (biflag), ODBC_INI); } #endif + jdflag[0] = '\0'; + getdsnattr(buf, "jdconv", jdflag, sizeof (jdflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !jdflag[0]) { + SQLGetPrivateProfileString(dsn, "jdconv", "", + jdflag, sizeof (jdflag), ODBC_INI); + } +#endif pwd[0] = '\0'; getdsnattr(buf, "pwd", pwd, sizeof (pwd)); #ifndef WITHOUT_DRIVERMGR @@ -11047,10 +12951,11 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, "DSN=%s;Database=%s;StepAPI=%s;Timeout=%s;" "SyncPragma=%s;NoTXN=%s;ShortNames=%s;LongNames=%s;" "NoCreat=%s;NoWCHAR=%s;FKSupport=%s;Tracefile=%s;" - "JournalMode=%s;LoadExt=%s;BigInt=%s;PWD=%s", + "JournalMode=%s;LoadExt=%s;BigInt=%s;JDConv=%s;" + "PWD=%s", dsn, dbname, sflag, busy, spflag, ntflag, snflag, lnflag, ncflag, nwflag, fkflag, tracef, - jmode, loadext, biflag,pwd); + jmode, loadext, biflag, jdflag, pwd); if (count < 0) { buf[sizeof (buf) - 1] = '\0'; } @@ -11072,6 +12977,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, d->nowchar = getbool(nwflag); d->fksupport = getbool(fkflag); d->dobigint = getbool(biflag); + d->jdconv = getbool(jdflag); d->oemcp = 0; d->pwdLen = strlen(pwd); d->pwd = (d->pwdLen > 0) ? pwd : NULL; @@ -11164,7 +13070,10 @@ drvallocstmt(SQLHDBC dbc, SQLHSTMT *stmt) memset(s, 0, sizeof (STMT)); s->dbc = dbc; s->ov3 = d->ov3; + s->bkmrk = SQL_UB_OFF; + s->bkmrkptr = 0; s->oemcp = &d->oemcp; + s->jdconv = &d->jdconv; s->nowchar[0] = d->nowchar; s->nowchar[1] = 0; s->dobigint = d->dobigint; @@ -11178,6 +13087,9 @@ drvallocstmt(SQLHDBC dbc, SQLHSTMT *stmt) s->bind_offs = NULL; s->paramset_size = 1; s->parm_bind_type = SQL_PARAM_BIND_BY_COLUMN; + s->one_tbl = -1; + s->has_pk = -1; + s->has_rowid = -1; #ifdef _WIN64 sprintf((char *) s->cursorname, "CUR_%I64X", (SQLUBIGINT) *stmt); #else @@ -11680,6 +13592,9 @@ freeresult(STMT *s, int clrcols) s->cols = NULL; s->ncols = 0; s->nowchar[1] = 0; + s->one_tbl = -1; + s->has_pk = -1; + s->has_rowid = -1; } } @@ -11693,14 +13608,8 @@ unbindcols(STMT *s) { int i; - s->bkmrkcol.type = -1; - s->bkmrkcol.max = 0; - s->bkmrkcol.lenp = NULL; - s->bkmrkcol.valp = NULL; - s->bkmrkcol.index = 0; - s->bkmrkcol.offs = 0; for (i = 0; s->bindcols && i < s->nbindcols; i++) { - s->bindcols[i].type = -1; + s->bindcols[i].type = SQL_UNKNOWN_TYPE; s->bindcols[i].max = 0; s->bindcols[i].lenp = NULL; s->bindcols[i].valp = NULL; @@ -11729,7 +13638,7 @@ mkbindcols(STMT *s, int ncols) return nomem(s); } for (i = s->nbindcols; i < ncols; i++) { - bindcols[i].type = -1; + bindcols[i].type = SQL_UNKNOWN_TYPE; bindcols[i].max = 0; bindcols[i].lenp = NULL; bindcols[i].valp = NULL; @@ -11769,27 +13678,34 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, { char **data, valdummy[16]; SQLLEN dummy; + SQLINTEGER *ilenp = NULL; int valnull = 0; int type = otype; + SQLRETURN sret = SQL_NO_DATA; if (!lenp) { lenp = &dummy; } - if (!s->rows) { - *lenp = SQL_NULL_DATA; - return SQL_NO_DATA; + /* workaround for JDK 1.7.0 on x86_64 */ + if (((SQLINTEGER *) lenp) + 1 == (SQLINTEGER *) val) { + ilenp = (SQLINTEGER *) lenp; + lenp = &dummy; } if (col >= s->ncols) { setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); return SQL_ERROR; } - if (s->rowp < 0 || s->rowp >= s->nrows) { - *lenp = SQL_NULL_DATA; - return SQL_NO_DATA; - } if (s->retr_data != SQL_RD_ON) { return SQL_SUCCESS; } + if (!s->rows) { + *lenp = SQL_NULL_DATA; + goto done; + } + if (s->rowp < 0 || s->rowp >= s->nrows) { + *lenp = SQL_NULL_DATA; + goto done; + } type = mapdeftype(type, s->cols[col].type, s->cols[col].nosign ? 1 : 0, s->nowchar[0]); #if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) @@ -11812,12 +13728,12 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, #ifdef SQL_BIT case SQL_C_BIT: #endif - *((char *) val) = 0; + *((SQLCHAR *) val) = 0; break; case SQL_C_USHORT: case SQL_C_SHORT: case SQL_C_SSHORT: - *((short *) val) = 0; + *((SQLSMALLINT *) val) = 0; break; case SQL_C_ULONG: case SQL_C_LONG: @@ -11838,7 +13754,7 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, break; case SQL_C_BINARY: case SQL_C_CHAR: - *((char *) val) = '\0'; + *((SQLCHAR *) val) = '\0'; break; #ifdef WCHARSUPPORT case SQL_C_WCHAR: @@ -11878,27 +13794,27 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, case SQL_C_UTINYINT: case SQL_C_TINYINT: case SQL_C_STINYINT: - *((char *) val) = strtol(*data, &endp, 0); + *((SQLCHAR *) val) = strtol(*data, &endp, 0); if (endp && endp == *data) { *lenp = SQL_NULL_DATA; } else { - *lenp = sizeof (char); + *lenp = sizeof (SQLCHAR); } break; #ifdef SQL_BIT case SQL_C_BIT: - *((char *) val) = getbool(*data); - *lenp = sizeof (char); + *((SQLCHAR *) val) = getbool(*data); + *lenp = sizeof (SQLCHAR); break; #endif case SQL_C_USHORT: case SQL_C_SHORT: case SQL_C_SSHORT: - *((short *) val) = strtol(*data, &endp, 0); + *((SQLSMALLINT *) val) = strtol(*data, &endp, 0); if (endp && endp == *data) { *lenp = SQL_NULL_DATA; } else { - *lenp = sizeof (short); + *lenp = sizeof (SQLSMALLINT); } break; case SQL_C_ULONG: @@ -11948,7 +13864,7 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, if (endp && endp == *data) { *lenp = SQL_NULL_DATA; } else { - *lenp = sizeof (int); + *lenp = sizeof (SQLBIGINT); } #endif break; @@ -12001,7 +13917,7 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, dlen -= 2; dp += 2; dlen = dlen / 2; - s->bincache = bin = xmalloc(dlen); + s->bincache = bin = xmalloc(dlen + 1); if (!bin) { return nomem(s); } @@ -12036,10 +13952,12 @@ converr: *lenp = 0; if (!dlen && s->bindcols[col].offs == dlen) { s->bindcols[col].offs = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } s->bindcols[col].offs = 0; - return SQL_NO_DATA; + sret = SQL_NO_DATA; + goto done; } offs = s->bindcols[col].offs; dlen -= offs; @@ -12063,14 +13981,16 @@ converr: if (s->bindcols[col].lenp) { *s->bindcols[col].lenp = dlen; } - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } s->bindcols[col].offs += *lenp; } if (*lenp == SQL_NO_TOTAL) { *lenp = dlen; setstat(s, -1, "data right truncated", "01004"); - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } break; } @@ -12096,7 +14016,8 @@ converr: ((char *) val)[0] = data[0][0]; memset((char *) val + 1, 0, len - 1); *lenp = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } } #endif @@ -12152,10 +14073,12 @@ converr: } if (!dlen && s->bindcols[col].offs == dlen) { s->bindcols[col].offs = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } s->bindcols[col].offs = 0; - return SQL_NO_DATA; + sret = SQL_NO_DATA; + goto done; } offs = s->bindcols[col].offs; dlen -= offs; @@ -12204,14 +14127,16 @@ converr: if (s->bindcols[col].lenp) { *s->bindcols[col].lenp = dlen; } - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } s->bindcols[col].offs += *lenp; } if (*lenp == SQL_NO_TOTAL) { *lenp = dlen; setstat(s, -1, "data right truncated", "01004"); - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } break; } @@ -12219,7 +14144,7 @@ converr: case SQL_C_TYPE_DATE: #endif case SQL_C_DATE: - if (str2date(*data, (DATE_STRUCT *) val) < 0) { + if (str2date(*s->jdconv, *data, (DATE_STRUCT *) val) < 0) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (DATE_STRUCT); @@ -12229,7 +14154,7 @@ converr: case SQL_C_TYPE_TIME: #endif case SQL_C_TIME: - if (str2time(*data, (TIME_STRUCT *) val) < 0) { + if (str2time(*s->jdconv, *data, (TIME_STRUCT *) val) < 0) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (TIME_STRUCT); @@ -12239,7 +14164,8 @@ converr: case SQL_C_TYPE_TIMESTAMP: #endif case SQL_C_TIMESTAMP: - if (str2timestamp(*data, (TIMESTAMP_STRUCT *) val) < 0) { + if (str2timestamp(*s->jdconv, *data, + (TIMESTAMP_STRUCT *) val) < 0) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (TIMESTAMP_STRUCT); @@ -12262,11 +14188,16 @@ converr: return SQL_ERROR; } } - return SQL_SUCCESS; + sret = SQL_SUCCESS; +done: + if (ilenp) { + *ilenp = *lenp; + } + return sret; } /** - * Interal bind C variable to column of result set. + * Internal bind C variable to column of result set. * @param stmt statement handle * @param col column number, starting at 1 * @param type output type @@ -12288,13 +14219,26 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, } s = (STMT *) stmt; if (col < 1) { - if (col == 0 && s->bkmrk && type == SQL_C_BOOKMARK) { - s->bkmrkcol.type = type; - s->bkmrkcol.max = sizeof (SQLINTEGER); - s->bkmrkcol.lenp = lenp; + if (col == 0 && s->bkmrk == SQL_UB_ON && + type == SQL_C_BOOKMARK) { + s->bkmrkcol.type = val ? type : SQL_UNKNOWN_TYPE; + s->bkmrkcol.max = val ? sizeof (SQLINTEGER) : 0; + s->bkmrkcol.lenp = val ? lenp : 0; s->bkmrkcol.valp = val; s->bkmrkcol.offs = 0; - if (lenp) { + if (val && lenp) { + *lenp = 0; + } + return SQL_SUCCESS; + } else if (col == 0 && s->bkmrk == SQL_UB_VARIABLE && + type == SQL_C_VARBOOKMARK && + max >= sizeof (sqlite_int64)) { + s->bkmrkcol.type = val ? type : SQL_UNKNOWN_TYPE; + s->bkmrkcol.max = val ? max : 0; + s->bkmrkcol.lenp = val ? lenp : 0; + s->bkmrkcol.valp = val; + s->bkmrkcol.offs = 0; + if (val && lenp) { *lenp = 0; } return SQL_SUCCESS; @@ -12324,7 +14268,7 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, case SQL_C_SHORT: case SQL_C_USHORT: case SQL_C_SSHORT: - sz = sizeof (short); + sz = sizeof (SQLSMALLINT); break; case SQL_C_FLOAT: sz = sizeof (SQLFLOAT); @@ -12343,7 +14287,7 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, break; case SQL_C_CHAR: break; -#ifdef WINTERFACE +#ifdef WCHARSUPPORT case SQL_C_WCHAR: break; #endif @@ -12385,7 +14329,7 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, } if (val == NULL) { /* unbind column */ - s->bindcols[col].type = -1; + s->bindcols[col].type = SQL_UNKNOWN_TYPE; s->bindcols[col].max = 0; s->bindcols[col].lenp = NULL; s->bindcols[col].valp = NULL; @@ -12509,7 +14453,7 @@ drvtables(SQLHSTMT stmt, s->rowfree = sqlite3_free; #endif s->nrows = 2; - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_SUCCESS; } if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') { @@ -12631,7 +14575,7 @@ doit: sqlite3_free(errp); errp = NULL; } - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_SUCCESS; } @@ -13517,7 +15461,6 @@ drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype) { SQLRETURN ret; STMT *s; - DBC *d; int asize; ret = mkresultset(stmt, typeSpec2, array_size(typeSpec2), @@ -13526,7 +15469,6 @@ drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype) return ret; } s = (STMT *) stmt; - d = (DBC *) s->dbc; #ifdef SQL_LONGVARCHAR s->nrows = (sqltype == SQL_ALL_TYPES) ? 13 : 1; #else @@ -14208,13 +16150,34 @@ SQLGetData(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, return SQL_INVALID_HANDLE; } s = (STMT *) stmt; - if (col == 0 && s->bkmrk && type == SQL_C_BOOKMARK) { - *((long *) val) = s->rowp; - if (lenp) { - *lenp = sizeof (long); + if (col == 0 && s->bkmrk != SQL_UB_OFF) { + if (s->bkmrk == SQL_UB_ON && type == SQL_C_BOOKMARK) { + *((SQLINTEGER *) val) = s->rowp; + if (lenp) { + *lenp = sizeof (SQLINTEGER); + } + ret = SQL_SUCCESS; + goto done; + } else if (s->bkmrk == SQL_UB_VARIABLE && type == SQL_C_VARBOOKMARK) { + if (s->has_rowid >= 0) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (s->rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + *((sqlite_int64 *) val) = strtol(*data, &endp, 0); +#else + *((sqlite_int64 *) val) = strtoll(*data, &endp, 0); +#endif + } else { + *((sqlite_int64 *) val) = s->rowp; + } + if (lenp) { + *lenp = sizeof (sqlite_int64); + } + ret = SQL_SUCCESS; + goto done; } - ret = SQL_SUCCESS; - goto done; } if (col < 1 || col > s->ncols) { setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); @@ -14240,18 +16203,50 @@ dofetchbind(STMT *s, int rsi) int ret, i, withinfo = 0; s->row_status0[rsi] = SQL_ROW_SUCCESS; - if (s->bkmrk && s->bkmrkcol.valp) { - long *val; + if (s->bkmrk != SQL_UB_OFF && s->bkmrkcol.valp) { + int bsize = sizeof (SQLINTEGER); - if (s->bind_type != SQL_BIND_BY_COLUMN) { - val = (long *) ((char *) s->bkmrkcol.valp + s->bind_type * rsi); + if (s->bkmrkcol.type == SQL_C_VARBOOKMARK) { + SQLPOINTER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * rsi); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * rsi); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->has_rowid >= 0) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (s->rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + *(sqlite_int64 *) val = strtol(*data, &endp, 0); +#else + *(sqlite_int64 *) val = strtoll(*data, &endp, 0); +#endif + } else { + *(sqlite_int64 *) val = s->rowp; + } + bsize = sizeof (sqlite_int64); } else { - val = (long *) s->bkmrkcol.valp + rsi; - } - if (s->bind_offs) { - val = (long *) ((char *) val + *s->bind_offs); + SQLINTEGER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLINTEGER *) + ((char *) s->bkmrkcol.valp + s->bind_type * rsi); + } else { + val = (SQLINTEGER *) s->bkmrkcol.valp + rsi; + } + if (s->bind_offs) { + val = (SQLINTEGER *) ((char *) val + *s->bind_offs); + } + *val = s->rowp; } - *val = s->rowp; if (s->bkmrkcol.lenp) { SQLLEN *ival; @@ -14264,7 +16259,7 @@ dofetchbind(STMT *s, int rsi) if (s->bind_offs) { ival = (SQLLEN *) ((char *) ival + *s->bind_offs); } - *ival = sizeof (long); + *ival = bsize; } } ret = SQL_SUCCESS; @@ -14352,7 +16347,7 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) i = 0; goto done2; } - if (!s->isselect) { + if (s->isselect != 1 && s->isselect != -1) { setstat(s, -1, "no result set available", "24000"); ret = SQL_ERROR; i = s->nrows; @@ -14367,7 +16362,7 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) ret = SQL_SUCCESS; i = 0; if (((DBC *) (s->dbc))->cur_s3stmt == s && s->s3stmt) { - s->rowp = 0; + s->rowp = s->rowprs = 0; for (; i < s->rowset_size; i++) { if (s->max_rows && s->s3stmt_rownum + 1 >= s->max_rows) { ret = (i == 0) ? SQL_NO_DATA : SQL_SUCCESS; @@ -14398,18 +16393,18 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) s->rowp = -1; } if (s->rowp >= s->nrows) { - s->rowp = s->nrows; + s->rowp = s->rowprs = s->nrows; return SQL_NO_DATA; } break; case SQL_FETCH_PRIOR: - if (s->nrows < 1) { - s->rowp = -1; + if (s->nrows < 1 || s->rowp <= 0) { + s->rowp = s->rowprs = -1; return SQL_NO_DATA; } s->rowp -= s->rowset_size + 1; if (s->rowp < -1) { - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_NO_DATA; } break; @@ -14430,17 +16425,17 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) break; case SQL_FETCH_ABSOLUTE: if (offset == 0) { - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_NO_DATA; } else if (offset < 0) { if (0 - offset <= s->nrows) { s->rowp = s->nrows + offset - 1; break; } - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_NO_DATA; } else if (offset > s->nrows) { - s->rowp = s->nrows; + s->rowp = s->rowprs = s->nrows; return SQL_NO_DATA; } s->rowp = offset - 1 - 1; @@ -14449,31 +16444,66 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) if (offset >= 0) { s->rowp += offset * s->rowset_size - 1; if (s->rowp >= s->nrows) { - s->rowp = s->nrows; + s->rowp = s->rowprs = s->nrows; return SQL_NO_DATA; } } else { s->rowp += offset * s->rowset_size - 1; if (s->rowp < -1) { - s->rowp = -1; + s->rowp = s->rowprs = -1; return SQL_NO_DATA; } } break; case SQL_FETCH_BOOKMARK: - if (s->bkmrk) { + if (s->bkmrk == SQL_UB_ON && !s->bkmrkptr) { if (offset < 0 || offset >= s->nrows) { return SQL_NO_DATA; } s->rowp = offset - 1; break; } + if (s->bkmrk != SQL_UB_OFF && s->bkmrkptr) { + int rowp; + + if (s->bkmrk == SQL_UB_VARIABLE) { + if (s->has_rowid >= 0) { + sqlite_int64 bkmrk, rowid; + + bkmrk = *(sqlite_int64 *) s->bkmrkptr; + for (rowp = 0; rowp < s->nrows; rowp++) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + rowid = strtol(*data, &endp, 0); +#else + rowid = strtoll(*data, &endp, 0); +#endif + if (rowid == bkmrk) { + break; + } + } + } else { + rowp = *(sqlite_int64 *) s->bkmrkptr; + } + } else { + rowp = *(int *) s->bkmrkptr; + } + if (rowp + offset < 0 || rowp + offset >= s->nrows) { + return SQL_NO_DATA; + } + s->rowp = rowp + offset - 1; + break; + } /* fall through */ default: s->row_status0[0] = SQL_ROW_ERROR; ret = SQL_ERROR; goto done; } + s->rowprs = s->rowp + 1; for (; i < s->rowset_size; i++) { ++s->rowp; if (s->rowp < 0 || s->rowp >= s->nrows) { @@ -14562,6 +16592,7 @@ SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT orient, SQLROWOFFSET offset, STMT *s; SQLRETURN ret; SQLUSMALLINT *rst; + SQLINTEGER *bkmrkptr; HSTMT_LOCK(stmt); if (stmt == SQL_NULL_HSTMT) { @@ -14571,8 +16602,11 @@ SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT orient, SQLROWOFFSET offset, /* temporarily turn off SQL_ATTR_ROW_STATUS_PTR */ rst = s->row_status; s->row_status = 0; + bkmrkptr = s->bkmrkptr; + s->bkmrkptr = 0; ret = drvfetchscroll(stmt, orient, offset); s->row_status = rst; + s->bkmrkptr = bkmrkptr; if (rowstatus) { memcpy(rowstatus, s->row_status0, sizeof (SQLUSMALLINT) * s->rowset_size); @@ -14602,7 +16636,7 @@ SQLRowCount(SQLHSTMT stmt, SQLLEN *nrows) } s = (STMT *) stmt; if (nrows) { - *nrows = s->nrows; + *nrows = s->isselect ? 0 : s->nrows; } HSTMT_UNLOCK(stmt); return SQL_SUCCESS; @@ -16167,6 +18201,8 @@ setupdyncols(STMT *s, sqlite3_stmt *s3stmt, int *ncolsp) #else dyncols[i].autoinc = SQL_FALSE; dyncols[i].notnull = SQL_NULLABLE; + dyncols[i].ispk = -1; + dyncols[i].isrowid = -1; #endif dyncols[i].typename = xstrdup(typename); } @@ -16226,7 +18262,7 @@ noconn: } errp = NULL; freeresult(s, -1); - if (s->isselect > 0) { + if (s->isselect == 1) { int ret, ncols, nretry = 0; const char *rest; sqlite3_stmt *s3stmt = NULL; @@ -16328,11 +18364,13 @@ unbound: SQLLEN *lenp = p->lenp; if (lenp && *lenp < 0 && *lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && - *lenp != SQL_NTS && *lenp != SQL_NULL_DATA) { + *lenp != SQL_NTS && *lenp != SQL_NULL_DATA && + *lenp != SQL_DATA_AT_EXEC) { setstat(s, -1, "invalid length reference", "HY009"); return SQL_ERROR; } - if (lenp && *lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (lenp && (*lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -16348,6 +18386,7 @@ again: s3stmt_end(s); if (initial) { /* fixup data-at-execution parameters and alloc'ed blobs */ + s->pdcount = -1; for (i = 0; i < s->nparams; i++) { BINDPARM *p = &s->bindparms[i]; @@ -16356,7 +18395,8 @@ again: } freep(&p->parbuf); if (p->need <= 0 && - p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -16372,7 +18412,7 @@ again: } } freeresult(s, 0); - if (s->isselect > 0 && !d->intrans && + if (s->isselect == 1 && !d->intrans && s->curtype == SQL_CURSOR_FORWARD_ONLY && d->step_enable && s->nparams == 0 && d->cur_s3stmt == NULL) { s->nrows = -1; @@ -16397,7 +18437,8 @@ again: p->param = NULL; } freep(&p->parbuf); - if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { p->param = p->param0; } p->lenp = p->lenp0; @@ -16421,9 +18462,9 @@ again: errp = NULL; } s->rowfree = freerows; - if (!s->isselect) { + if (s->isselect <= 0 || s->isselect > 1) { /* - * INSERT/UPDATE/DELETE results are immediately released. + * INSERT/UPDATE/DELETE or DDL results are immediately released. */ freeresult(s, -1); nrows += sqlite3_changes(d->sqlite); @@ -16443,7 +18484,7 @@ done: mkbindcols(s, s->ncols); done2: ret = SQL_SUCCESS; - s->rowp = -1; + s->rowp = s->rowprs = -1; s->paramset_count++; s->paramset_nrows = s->nrows; if (s->paramset_count < s->paramset_size) { @@ -16461,7 +18502,8 @@ done2: } else if (p->lenp0 && p->inc > 0) { p->lenp = p->lenp0 + s->paramset_count; } - if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { if (p->param0 && s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) { p->param = (char *) p->param0 + @@ -16470,7 +18512,8 @@ done2: p->param = (char *) p->param0 + s->paramset_count * p->inc; } - } else if (p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + } else if (p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -16487,7 +18530,8 @@ cleanup: p->param = NULL; } freep(&p->parbuf); - if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { p->param = p->param0; } p->lenp = p->lenp0; @@ -16499,7 +18543,12 @@ cleanup: s->paramset_count = 0; s->paramset_nrows = 0; } - if (*s->ov3 && !s->isselect && ret == SQL_SUCCESS && nrows == 0) { + /* + * For INSERT/UPDATE/DELETE statements change the return code + * to SQL_NO_DATA if the number of rows affected was 0. + */ + if (*s->ov3 && s->isselect == 0 && + ret == SQL_SUCCESS && nrows == 0) { ret = SQL_NO_DATA; } return ret; @@ -16682,12 +18731,12 @@ done: #include <windowsx.h> #include <winuser.h> -#define MAXPATHLEN (255+1) /* Max path length */ +#define MAXPATHLEN (259+1) /* Max path length */ #define MAXKEYLEN (15+1) /* Max keyword length */ #define MAXDESC (255+1) /* Max description length */ #define MAXDSNAME (32+1) /* Max data source name length */ #define MAXTONAME (32+1) /* Max timeout length */ -#define MAXDBNAME (255+1) +#define MAXDBNAME MAXPATHLEN /* Attribute key indexes into an array of Attr structs, see below */ @@ -16709,7 +18758,8 @@ done: #define KEY_OEMCP 15 #define KEY_BIGINT 16 #define KEY_PASSWD 17 -#define NUMOFKEYS 18 +#define KEY_JDCONV 18 +#define NUMOFKEYS 19 typedef struct { BOOL supplied; @@ -16748,6 +18798,7 @@ static struct { { "OEMCP", KEY_OEMCP }, { "BigInt", KEY_BIGINT }, { "PWD", KEY_PASSWD }, + { "JDConv", KEY_JDCONV }, { NULL, 0 } }; @@ -16895,6 +18946,11 @@ SetDSNAttributes(HWND parent, SETUPDLG *setupdlg) setupdlg->attr[KEY_BIGINT].attr, ODBC_INI); } + if (parent || setupdlg->attr[KEY_JDCONV].supplied) { + SQLWritePrivateProfileString(dsn, "JDConv", + setupdlg->attr[KEY_JDCONV].attr, + ODBC_INI); + } if (parent || setupdlg->attr[KEY_PASSWD].supplied) { SQLWritePrivateProfileString(dsn, "PWD", setupdlg->attr[KEY_PASSWD].attr, @@ -17013,6 +19069,12 @@ GetAttributes(SETUPDLG *setupdlg) sizeof (setupdlg->attr[KEY_PASSWD].attr), ODBC_INI); } + if (!setupdlg->attr[KEY_JDCONV].supplied) { + SQLGetPrivateProfileString(dsn, "JDConv", "", + setupdlg->attr[KEY_JDCONV].attr, + sizeof (setupdlg->attr[KEY_JDCONV].attr), + ODBC_INI); + } } /** @@ -17066,7 +19128,7 @@ ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) switch (wmsg) { case WM_INITDIALOG: #ifdef _WIN64 - SetWindowLong(hdlg, DWLP_USER, lparam); + SetWindowLongPtr(hdlg, DWLP_USER, lparam); #else SetWindowLong(hdlg, DWL_USER, lparam); #endif @@ -17114,6 +19176,9 @@ ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) CheckDlgButton(hdlg, IDC_BIGINT, getbool(setupdlg->attr[KEY_BIGINT].attr) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_JDCONV, + getbool(setupdlg->attr[KEY_JDCONV].attr) ? + BST_CHECKED : BST_UNCHECKED); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); SendDlgItemMessage(hdlg, IDC_SYNCP, @@ -17202,6 +19267,9 @@ ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) strcpy(setupdlg->attr[KEY_BIGINT].attr, (IsDlgButtonChecked(hdlg, IDC_BIGINT) == BST_CHECKED) ? "1" : "0"); + strcpy(setupdlg->attr[KEY_JDCONV].attr, + (IsDlgButtonChecked(hdlg, IDC_JDCONV) == BST_CHECKED) ? + "1" : "0"); SetDSNAttributes(hdlg, setupdlg); /* FALL THROUGH */ case IDCANCEL: @@ -17285,7 +19353,7 @@ DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) switch (wmsg) { case WM_INITDIALOG: #ifdef _WIN64 - SetWindowLong(hdlg, DWLP_USER, lparam); + SetWindowLongPtr(hdlg, DWLP_USER, lparam); #else SetWindowLong(hdlg, DWL_USER, lparam); #endif @@ -17332,6 +19400,9 @@ DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) CheckDlgButton(hdlg, IDC_BIGINT, getbool(setupdlg->attr[KEY_BIGINT].attr) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_JDCONV, + getbool(setupdlg->attr[KEY_JDCONV].attr) ? + BST_CHECKED : BST_UNCHECKED); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); SendDlgItemMessage(hdlg, IDC_SYNCP, @@ -17405,6 +19476,9 @@ DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) strcpy(setupdlg->attr[KEY_BIGINT].attr, (IsDlgButtonChecked(hdlg, IDC_BIGINT) == BST_CHECKED) ? "1" : "0"); + strcpy(setupdlg->attr[KEY_JDCONV].attr, + (IsDlgButtonChecked(hdlg, IDC_JDCONV) == BST_CHECKED) ? + "1" : "0"); /* FALL THROUGH */ case IDCANCEL: EndDialog(hdlg, GET_WM_COMMAND_ID(wparam, lparam) == IDOK); @@ -17433,7 +19507,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, SQLCHAR *connOut, SQLSMALLINT connOutMax, SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) { - BOOL maybeprompt, prompt = FALSE; + BOOL maybeprompt, prompt = FALSE, defaultdsn = FALSE; DBC *d; SETUPDLG *setupdlg; SQLRETURN ret; @@ -17462,6 +19536,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, if (!setupdlg->attr[KEY_DSN].attr[0] && drvcompl == SQL_DRIVER_COMPLETE_REQUIRED) { strcpy(setupdlg->attr[KEY_DSN].attr, "DEFAULT"); + defaultdsn = TRUE; } GetAttributes(setupdlg); if (drvcompl == SQL_DRIVER_PROMPT || @@ -17488,9 +19563,9 @@ retry: driver = setupdlg->attr[KEY_DRIVER].attr; dbname = setupdlg->attr[KEY_DBNAME].attr; if (connOut || connOutLen) { - char buf[2048]; + char buf[SQL_MAX_MESSAGE_LENGTH * 4]; int len, count; - char dsn_0 = dsn ? dsn[0] : '\0'; + char dsn_0 = (dsn && !defaultdsn) ? dsn[0] : '\0'; char drv_0 = driver ? driver[0] : '\0'; buf[0] = '\0'; @@ -17500,7 +19575,7 @@ retry: "ShortNames=%s;LongNames=%s;" "NoCreat=%s;NoWCHAR=%s;" "FKSupport=%s;JournalMode=%s;OEMCP=%s;LoadExt=%s;" - "BigInt=%s;PWD=%s", + "BigInt=%s;JDConv=%s;PWD=%s", dsn_0 ? "DSN=" : "", dsn_0 ? dsn : "", dsn_0 ? ";" : "", @@ -17521,6 +19596,7 @@ retry: setupdlg->attr[KEY_OEMCP].attr, setupdlg->attr[KEY_LOADEXT].attr, setupdlg->attr[KEY_BIGINT].attr, + setupdlg->attr[KEY_JDCONV].attr, setupdlg->attr[KEY_PASSWD].attr); if (count < 0) { buf[sizeof (buf) - 1] = '\0'; @@ -17552,6 +19628,7 @@ retry: d->fksupport = getbool(setupdlg->attr[KEY_FKSUPPORT].attr); d->oemcp = getbool(setupdlg->attr[KEY_OEMCP].attr); d->dobigint = getbool(setupdlg->attr[KEY_BIGINT].attr); + d->jdconv = getbool(setupdlg->attr[KEY_JDCONV].attr); d->pwdLen = strlen(setupdlg->attr[KEY_PASSWD].attr); d->pwd = (d->pwdLen > 0) ? setupdlg->attr[KEY_PASSWD].attr : NULL; ret = dbopen(d, dbname ? dbname : "", 0, @@ -17636,6 +19713,9 @@ SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd, HDBC_LOCK(dbc); if (connIn) { #if defined(_WIN32) || defined(_WIN64) + if (connInLen == SQL_NTS) { + connInLen = -1; + } ci = uc_to_wmb(connIn, connInLen); #else ci = uc_to_utf(connIn, connInLen); @@ -17663,9 +19743,8 @@ SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd, co = uc_from_utf((SQLCHAR *) connOut, len); #endif if (co) { - uc_strncpy(connOut, co, connOutMax); - co[len] = 0; - len = min(connOutMax, uc_strlen(co)); + uc_strncpy(connOut, co, connOutMax / sizeof (SQLWCHAR)); + len = min(connOutMax / sizeof (SQLWCHAR), uc_strlen(co)); uc_free(co); } else { len = 0; @@ -17890,7 +19969,7 @@ InUn(int remove, char *cmdline) if (GetFileAttributesA(dllbuf) == INVALID_FILE_ATTRIBUTES) { return FALSE; } - if (strcmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) { + if (strcasecmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) { char buf[512]; sprintf(buf, "Copy %s to %s failed.", dllbuf, inst); @@ -18244,6 +20323,18 @@ dls_0(void) return 0; } +static sqlite_int64 +dls_0LL(void) +{ + return 0; +} + +static double +dls_00(void) +{ + return 0; +} + static void * dls_null(void) { @@ -18293,6 +20384,7 @@ static struct { DLS_ENT(column_count, dls_0), DLS_ENT(column_database_name, dls_empty), DLS_ENT(column_decltype, dls_empty), + DLS_ENT(column_double, dls_00), DLS_ENT(column_name, dls_empty), DLS_ENT(column_origin_name, dls_null), DLS_ENT(column_table_name, dls_null), @@ -18309,6 +20401,7 @@ static struct { DLS_ENT(get_table, dls_error), DLS_ENT(interrupt, dls_void), DLS_ENT(key, dls_error), + DLS_ENT(last_insert_rowid, dls_0LL), DLS_ENT(libversion, dls_empty), DLS_ENT(load_extension, dls_error), DLS_ENT(malloc, malloc), diff --git a/lang/sql/odbc/sqlite3odbc.h b/lang/sql/odbc/sqlite3odbc.h index a1d2d4c4..f6c81b32 100644 --- a/lang/sql/odbc/sqlite3odbc.h +++ b/lang/sql/odbc/sqlite3odbc.h @@ -15,9 +15,9 @@ * @file sqlite3odbc.h * Header file for SQLite3 ODBC driver. * - * $Id: sqlite3odbc.h,v 1.38 2011/11/08 17:02:04 chw Exp chw $ + * $Id: sqlite3odbc.h,v 1.43 2013/09/23 09:20:26 chw Exp chw $ * - * Copyright (c) 2004-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2004-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a @@ -138,6 +138,7 @@ typedef struct dbc { int step_enable; /**< True for sqlite_compile/step/finalize */ int trans_disable; /**< True for no transaction support */ int oemcp; /**< True for Win32 OEM CP translation */ + int jdconv; /**< True for julian day conversion */ struct stmt *cur_s3stmt; /**< Current STMT executing sqlite statement */ int s3stmt_needmeta; /**< True to get meta data in s3stmt_step(). */ FILE *trace; /**< sqlite3_trace() file pointer or NULL */ @@ -170,6 +171,8 @@ typedef struct { int prec; /**< Precision of column */ int autoinc; /**< AUTO_INCREMENT column */ int notnull; /**< NOT NULL constraint on column */ + int ispk; /**< Flag for primary key (> 0) */ + int isrowid; /**< Flag for ROWID column (> 0) */ char *typename; /**< Column type name or NULL */ char *label; /**< Column label or NULL */ } COL; @@ -230,20 +233,24 @@ typedef struct stmt { SQLCHAR *query; /**< Current query, raw string */ int *ov3; /**< True for SQL_OV_ODBC3 */ int *oemcp; /**< True for Win32 OEM CP translation */ + int *jdconv; /**< True for julian day conversion */ int isselect; /**< > 0 if query is a SELECT statement */ int ncols; /**< Number of result columns */ COL *cols; /**< Result column array */ COL *dyncols; /**< Column array, but malloc()ed */ int dcols; /**< Number of entries in dyncols */ int bkmrk; /**< True when bookmarks used */ + SQLINTEGER *bkmrkptr; /**< SQL_ATTR_FETCH_BOOKMARK_PTR */ BINDCOL bkmrkcol; /**< Bookmark bound column */ BINDCOL *bindcols; /**< Array of bound columns */ int nbindcols; /**< Number of entries in bindcols */ int nbindparms; /**< Number bound parameters */ BINDPARM *bindparms; /**< Array of bound parameters */ int nparams; /**< Number of parameters in query */ + int pdcount; /**< SQLParamData() counter */ int nrows; /**< Number of result rows */ int rowp; /**< Current result row */ + int rowprs; /**< Current start row of rowset */ char **rows; /**< 2-dim array, result set */ void (*rowfree)(); /**< Free function for rows */ int naterr; /**< Native error code */ @@ -279,6 +286,9 @@ typedef struct stmt { char *bincache; /**< Cache for blob data */ int binlen; /**< Length of blob data */ int guessed_types; /**< Flag for drvprepare()/drvexecute() */ + int one_tbl; /**< Flag for single table (> 0) */ + int has_pk; /**< Flag for primary key (> 0) */ + int has_rowid; /**< Flag for ROWID (>= 0 or -1) */ } STMT; #endif diff --git a/lang/sql/odbc/sqlite3odbc.mak b/lang/sql/odbc/sqlite3odbc.mak new file mode 100644 index 00000000..bafe7833 --- /dev/null +++ b/lang/sql/odbc/sqlite3odbc.mak @@ -0,0 +1,95 @@ +# VC++ 6 Makefile +# uses the SQLite3 amalgamation source which must +# be unpacked below in the same folder as this makefile + +CC= cl +LN= link +RC= rc + +!IF "$(DEBUG)" == "1" +LDEBUG= /DEBUG +CDEBUG= -Zi +!ELSE +LDEBUG= /RELEASE +!ENDIF + +CFLAGS= -I. -Gs -GX -D_WIN32 -D_DLL -nologo $(CDEBUG) \ + -DHAVE_SQLITE3COLUMNTABLENAME=1 \ + -DHAVE_SQLITE3PREPAREV2=1 \ + -DHAVE_SQLITE3VFS=1 \ + -DHAVE_SQLITE3LOADEXTENSION=1 \ + -DSQLITE_ENABLE_COLUMN_METADATA=1 \ + -DWITHOUT_SHELL=1 +CFLAGSEXE= -I. -Gs -GX -D_WIN32 -nologo $(CDEBUG) +DLLLFLAGS= /NODEFAULTLIB $(LDEBUG) /NOLOGO /MACHINE:IX86 \ + /SUBSYSTEM:WINDOWS /DLL +DLLLIBS= msvcrt.lib odbccp32.lib kernel32.lib \ + user32.lib comdlg32.lib + +DRVDLL= sqlite3odbc.dll + +OBJECTS= sqlite3odbc.obj sqlite3.obj + +.c.obj: + $(CC) $(CFLAGS) /c $< + +all: $(DRVDLL) inst.exe uninst.exe adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe SQLiteODBCInstaller.exe + +clean: + del *.obj + del *.res + del *.exp + del *.ilk + del *.pdb + del *.res + del resource3.h + del *.exe + cd .. + +uninst.exe: inst.exe + copy inst.exe uninst.exe + +inst.exe: inst.c + $(CC) $(CFLAGSEXE) inst.c odbc32.lib odbccp32.lib \ + kernel32.lib user32.lib + +remdsn.exe: adddsn.exe + copy adddsn.exe remdsn.exe + +adddsn.exe: adddsn.c + $(CC) $(CFLAGSEXE) adddsn.c odbc32.lib odbccp32.lib \ + kernel32.lib user32.lib + +remsysdsn.exe: adddsn.exe + copy adddsn.exe remsysdsn.exe + +addsysdsn.exe: adddsn.exe + copy adddsn.exe addsysdsn.exe + +fixup.exe: fixup.c + $(CC) $(CFLAGSEXE) fixup.c + +mkopc3.exe: mkopc3.c + $(CC) $(CFLAGSEXE) mkopc3.c + +SQLiteODBCInstaller.exe: SQLiteODBCInstaller.c + $(CC) $(CFLAGSEXE) SQLiteODBCInstaller.c \ + kernel32.lib user32.lib + +sqlite3odbc.c: resource3.h + +sqlite3odbc.res: sqlite3odbc.rc resource3.h + $(RC) -I. -fo sqlite3odbc.res -r sqlite3odbc.rc + +sqlite3odbc.dll: $(OBJECTS) sqlite3odbc.res + $(LN) $(DLLLFLAGS) $(OBJECTS) sqlite3odbc.res \ + -def:sqlite3odbc.def -out:$@ $(DLLLIBS) + +VERSION_C: fixup.exe VERSION + .\fixup < VERSION > VERSION_C . , + +resource3.h: resource.h.in VERSION_C fixup.exe + .\fixup < resource.h.in > resource3.h \ + --VERS-- @VERSION \ + --VERS_C-- @VERSION_C diff --git a/lang/sql/odbc/sqlite3odbc.rc b/lang/sql/odbc/sqlite3odbc.rc index 81b151ac..f27db636 100644 --- a/lang/sql/odbc/sqlite3odbc.rc +++ b/lang/sql/odbc/sqlite3odbc.rc @@ -12,7 +12,7 @@ // Dialog // -DRIVERCONNECT DIALOG DISCARDABLE 65, 43, 277, 175 +DRIVERCONNECT DIALOG DISCARDABLE 65, 43, 277, 190 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "SQLite3 ODBC Driver Connect" @@ -41,19 +41,21 @@ BEGIN CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,85,110,75,15 CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,170,110,52,15 - EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP - DEFPUSHBUTTON "OK",IDOK,175,150,40,14 - PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + WS_TABSTOP,170,110,75,15 + CONTROL "Julian Day Conv.",IDC_JDCONV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,125,75,15 + EDITTEXT IDC_LOADEXT,82,145,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,165,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,165,40,14 CTEXT "Enter options for connect",1003,95,6,80,8 RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP - RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,147,73,9,NOT WS_GROUP END -CONFIGDSN DIALOG DISCARDABLE 65, 43, 277, 175 +CONFIGDSN DIALOG DISCARDABLE 65, 43, 277, 190 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "SQLite3 ODBC DSN Configuration" @@ -82,16 +84,18 @@ BEGIN CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,85,110,75,15 CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,170,110,52,15 - EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP - DEFPUSHBUTTON "OK",IDOK,175,150,40,14 - PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + WS_TABSTOP,170,110,75,15 + CONTROL "Julian Day Conv.",IDC_JDCONV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,125,75,15 + EDITTEXT IDC_LOADEXT,82,145,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,165,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,165,40,14 CTEXT "Enter options for connect",1003,95,6,80,8 RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP - RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,147,73,9,NOT WS_GROUP END ///////////////////////////////////////////////////////////////////////////// @@ -116,7 +120,7 @@ BEGIN VALUE "FileDescription", "SQLite3 ODBC Driver\0" VALUE "FileVersion", VERSION "\0" VALUE "InternalName", "SQLITE3ODBC\0" - VALUE "LegalCopyright", "Copyright © 2004-2011 <chw@ch-werner.de>\0" + VALUE "LegalCopyright", "Copyright © 2004-2013 <chw@ch-werner.de>\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "SQLITE3ODBC.DLL\0" VALUE "PrivateBuild", "\0" diff --git a/lang/sql/odbc/sqlite3odbc.rc.in b/lang/sql/odbc/sqlite3odbc.rc.in index 07369eca..07832acb 100644 --- a/lang/sql/odbc/sqlite3odbc.rc.in +++ b/lang/sql/odbc/sqlite3odbc.rc.in @@ -12,7 +12,7 @@ // Dialog // -DRIVERCONNECT DIALOG DISCARDABLE 65, 43, 277, 175 +DRIVERCONNECT DIALOG DISCARDABLE 65, 43, 277, 190 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "SQLite3 ODBC Driver Connect" @@ -41,19 +41,21 @@ BEGIN CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,85,110,75,15 CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,170,110,52,15 - EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP - DEFPUSHBUTTON "OK",IDOK,175,150,40,14 - PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + WS_TABSTOP,170,110,75,15 + CONTROL "Julian Day Conv.",IDC_JDCONV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,125,75,15 + EDITTEXT IDC_LOADEXT,82,145,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,165,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,165,40,14 CTEXT "Enter options for connect",1003,95,6,80,8 RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP - RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,147,73,9,NOT WS_GROUP END -CONFIGDSN DIALOG DISCARDABLE 65, 43, 277, 175 +CONFIGDSN DIALOG DISCARDABLE 65, 43, 277, 190 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "SQLite3 ODBC DSN Configuration" @@ -82,16 +84,18 @@ BEGIN CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,85,110,75,15 CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,170,110,52,15 - EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP - DEFPUSHBUTTON "OK",IDOK,175,150,40,14 - PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + WS_TABSTOP,170,110,75,15 + CONTROL "Julian Day Conv.",IDC_JDCONV,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,125,75,15 + EDITTEXT IDC_LOADEXT,82,145,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,165,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,165,40,14 CTEXT "Enter options for connect",1003,95,6,80,8 RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP - RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,147,73,9,NOT WS_GROUP END ///////////////////////////////////////////////////////////////////////////// @@ -116,7 +120,7 @@ BEGIN VALUE "FileDescription", "SQLite3 ODBC Driver\0" VALUE "FileVersion", VERSION "\0" VALUE "InternalName", "SQLITE3ODBC\0" - VALUE "LegalCopyright", "Copyright © 2004-2011 <chw@ch-werner.de>\0" + VALUE "LegalCopyright", "Copyright © 2004-2013 <chw@ch-werner.de>\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "SQLITE3ODBC.DLL\0" VALUE "PrivateBuild", "\0" diff --git a/lang/sql/odbc/sqlite4odbc.c b/lang/sql/odbc/sqlite4odbc.c new file mode 100644 index 00000000..9cb60ca6 --- /dev/null +++ b/lang/sql/odbc/sqlite4odbc.c @@ -0,0 +1,19821 @@ +/** + * @file sqlite4odbc.c + * SQLite4 ODBC Driver main module. + * + * $Id: sqlite4odbc.c,v 1.8 2013/12/08 07:18:56 chw Exp chw $ + * + * Copyright (c) 2013 Christian Werner <chw@ch-werner.de> + * + * See the file "license.terms" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + */ + +#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_API) +#undef WITH_SQLITE_DLLS +#undef SQLITE_DYNLOAD +#include "sqlite4.c" +#endif + +#if defined(WITH_SQLITE_DLLS) && (WITH_SQLITE_DLLS > 1) +#define SQLITE_DYNLOAD 1 +#endif + +#include "sqlite4odbc.h" + +#ifdef SQLITE_DYNLOAD + +#undef MEMORY_DEBUG + +#if defined(_WIN32) || defined(_WIN64) +static void dls_init(void); +static void dls_fini(void); +#else +void dls_init(void); +void dls_fini(void); +#endif + +static struct dl_sqlite4_funcs { + int (*bind_blob)(sqlite4_stmt *p0, int p1, const void *p2, int p3, + void (*p4)(void *, void *), void *p5); + int (*bind_double)(sqlite4_stmt *p0, int p1, double p2); + int (*bind_int)(sqlite4_stmt *p0, int p1, int p2); + int (*bind_int64)(sqlite4_stmt *p0, int p1, sqlite4_int64 p2); + int (*bind_null)(sqlite4_stmt *p0, int p1); + int (*bind_parameter_count)(sqlite4_stmt *p0); + int (*bind_text)(sqlite4_stmt *p0, int p1, const char *p2, int p3, + void (*p4)(void *, void *), void *p5); + int (*changes)(sqlite4 *p0); + int (*close)(sqlite4 *p0, unsigned int p1); + const void * (*column_blob)(sqlite4_stmt *p0, int p1, int p2); + int (*column_bytes)(sqlite4_stmt *p0, int p1); + int (*column_count)(sqlite4_stmt *p0); + const char * (*column_database_name)(sqlite4_stmt *p0, int p1); + const char * (*column_decltype)(sqlite4_stmt *p0, int p1); + double (*column_double)(sqlite4_stmt *p0); + const char * (*column_name)(sqlite4_stmt *p0, int p1); + const char * (*column_origin_name)(sqlite4_stmt *p0, int p1); + const char * (*column_table_name)(sqlite4_stmt *p0, int p1); + const char * (*column_text)(sqlite4_stmt *p0, int p1, p2); + int (*column_type)(sqlite4_stmt *p0, int p1); + int (*create_function)(sqlite4 *p0, const char *p1, int p2, + void *p4, + void (*p5)(sqlite4_context *, int, sqlite4_value **), + void (*p6)(sqlite4_context *, int, sqlite4_value **), + void (*p7)(sqlite4_context *), + void (*p8)(void *)); + int (*errcode)(sqlite4 *p0); + const char * (*errmsg)(sqlite4 *p0); + int (*exec)(sqlite4 *p0, const char *p1, + int (*p2)(void *, int, char **, char **), + void *p3); + int (*finalize)(sqlite4_stmt *p0); + void (*free)(sqlite4_env *p0, void *p1); + void (*interrupt)(sqlite4 *p0); +#if 0 + sqlite4_int64 (*last_insert_rowid)(sqlite4 *p0); +#endif + const char * (*libversion)(void); +#if 0 + int (*load_extension)(sqlite4 *p0, const char *p1, const char *p2, + char **p3); +#endif + void * (*malloc)(sqlite4_env *p0, int p1); + char * (*mprintf)(sqlite4_env *p0, const char *p1, ...); + int (*open)(sqlite4_env *p0, const char *p1, sqlite4 **p2, ...); + int (*prepare)(sqlite4 *p0, const char *p1, int p2, sqlite4_stmt **p3, + int *p4); + void (*profile)(sqlite4 *p0, void *p1, + void (*p2)(void *, const char *, sqlite4_uint64), + void *p3); + void * (*realloc)(sqlite4_env *p0, void *p1, int p2); + int (*reset)(sqlite4_stmt *p0); + void (*result_blob)(sqlite4_context *p0, const void *p1, + int p2, void (*p3)(void *, void *), void *p4); + void (*result_error)(sqlite4_context *p0, const char *p1, int p2); + void (*result_int)(sqlite4_context *p0, int p1); + void (*result_null)(sqlite4_context *p0); + int (*step)(sqlite4_stmt *p0); + int (*xstrnicmp)(const char *p0, const char *p1, int p2); +#if 0 + int (*table_column_metadata)(sqlite4 *p0, const char *p1, + const char *p2, const char *p3, + char const **p4, char const **p5, + int *p6, int *p7, int *p8); +#endif + void (*trace)(sqlite4 *p0, void *p1, void (*p2)(void *, const char *), + void *p2); + void * (*user_data)(sqlite4_context *p0); + const void * (*value_blob)(sqlite4_value *p0, int *p1); + int (*value_bytes)(sqlite4_value *p0); + const char * (*value_text)(sqlite4_value *p0, int *p1); + int (*value_type)(sqlite4_value *p0); +} dls_funcs; + +#define sqlite4_bind_blob dls_funcs.bind_blob +#define sqlite4_bind_double dls_funcs.bind_double +#define sqlite4_bind_int dls_funcs.bind_int +#define sqlite4_bind_int64 dls_funcs.bind_int64 +#define sqlite4_bind_null dls_funcs.bind_null +#define sqlite4_bind_parameter_count dls_funcs.bind_parameter_count +#define sqlite4_bind_text dls_funcs.bind_text +#define sqlite4_changes dls_funcs.changes +#define sqlite4_close dls_funcs.close +#define sqlite4_column_blob dls_funcs.column_blob +#define sqlite4_column_bytes dls_funcs.column_bytes +#define sqlite4_column_count dls_funcs.column_count +#define sqlite4_column_database_name dls_funcs.column_database_name +#define sqlite4_column_decltype dls_funcs.column_decltype +#define sqlite4_column_double dls_funcs.column_double +#define sqlite4_column_name dls_funcs.column_name +#define sqlite4_column_origin_name dls_funcs.column_origin_name +#define sqlite4_column_table_name dls_funcs.column_table_name +#define sqlite4_column_text dls_funcs.column_text +#define sqlite4_column_type dls_funcs.column_type +#define sqlite4_create_function dls_funcs.create_function +#define sqlite4_errcode dls_funcs.errcode +#define sqlite4_errmsg dls_funcs.errmsg +#define sqlite4_exec dls_funcs.exec +#define sqlite4_finalize dls_funcs.finalize +#define sqlite4_free dls_funcs.free +#define sqlite4_interrupt dls_funcs.interrupt +#if 0 +#define sqlite4_last_insert_rowid dls_funcs.last_insert_rowid +#endif +#define sqlite4_libversion dls_funcs.libversion +#if 0 +#define sqlite4_load_extension dls_funcs.load_extension +#endif +#define sqlite4_malloc dls_funcs.malloc +#define sqlite4_mprintf dls_funcs.mprintf +#define sqlite4_open dls_funcs.open +#define sqlite4_prepare dls_funcs.prepare +#define sqlite4_profile dls_funcs.profile +#define sqlite4_realloc dls_funcs.realloc +#define sqlite4_reset dls_funcs.reset +#define sqlite4_result_blob dls_funcs.result_blob +#define sqlite4_result_error dls_funcs.result_error +#define sqlite4_result_int dls_funcs.result_int +#define sqlite4_result_null dls_funcs.result_null +#define sqlite4_step dls_funcs.step +#define sqlite4_strnicmp dls_funcs.xstrnicmp +#if 0 +#define sqlite4_table_column_metadata dls_funcs.table_column_metadata +#endif +#define sqlite4_trace dls_funcs.trace +#define sqlite4_user_data dls_funcs.user_data +#define sqlite4_value_blob dls_funcs.value_blob +#define sqlite4_value_bytes dls_funcs.value_bytes +#define sqlite4_value_text dls_funcs.value_text +#define sqlite4_value_type dls_funcs.value_type + +#endif + +#ifndef WITHOUT_WINTERFACE +#define WINTERFACE +#define WCHARSUPPORT +#endif + +#if !defined(_WIN32) && !defined(_WIN64) +#if !defined(WCHARSUPPORT) && defined(HAVE_SQLWCHAR) && (HAVE_SQLWCHAR) +#define WCHARSUPPORT +#endif +#endif + +#if defined(WINTERFACE) +#include <sqlucode.h> +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include "resource3.h" +#define ODBC_INI "ODBC.INI" +#ifndef DRIVER_VER_INFO +#define DRIVER_VER_INFO VERSION +#endif +#else +#define ODBC_INI ".odbc.ini" +#endif + +#ifndef DRIVER_VER_INFO +#define DRIVER_VER_INFO "0.0" +#endif + +#ifndef COLATTRIBUTE_LAST_ARG_TYPE +#ifdef _WIN64 +#define COLATTRIBUTE_LAST_ARG_TYPE SQLLEN * +#else +#define COLATTRIBUTE_LAST_ARG_TYPE SQLPOINTER +#endif +#endif + +#ifndef SETSTMTOPTION_LAST_ARG_TYPE +#define SETSTMTOPTION_LAST_ARG_TYPE SQLROWCOUNT +#endif + +#undef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#undef max +#define max(a, b) ((a) < (b) ? (b) : (a)) + +#ifndef PTRDIFF_T +#define PTRDIFF_T int +#endif + +#define array_size(x) (sizeof (x) / sizeof (x[0])) + +#define stringify1(s) #s +#define stringify(s) stringify1(s) + +#define verinfo(maj, min, lev) ((maj) << 16 | (min) << 8 | (lev)) + +/* Column types for static string column descriptions (SQLTables etc.) */ + +#if defined(WINTERFACE) && !defined(_WIN32) && !defined(_WIN64) +#define SCOL_VARCHAR SQL_WVARCHAR +#define SCOL_CHAR SQL_WCHAR +#else +#define SCOL_VARCHAR SQL_VARCHAR +#define SCOL_CHAR SQL_CHAR +#endif + +#define ENV_MAGIC 0x53544145 +#define DBC_MAGIC 0x53544144 +#define DEAD_MAGIC 0xdeadbeef + +/** + * @typedef dstr + * @struct dstr + * Internal structure representing dynamic strings. + */ + +typedef struct dstr { + int len; /**< Current length. */ + int max; /**< Maximum length of buffer. */ + int oom; /**< True when out of memory. */ + char buffer[1]; /**< String buffer. */ +} dstr; + +static const char *xdigits = "0123456789ABCDEFabcdef"; + +#ifdef MEMORY_DEBUG + +static void * +xmalloc_(int n, char *file, int line) +{ + int nn = n + 4 * sizeof (long); + long *p; + + p = malloc(nn); + if (!p) { +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "malloc\t%d\tNULL\t%s:%d\n", n, file, line); +#endif + return NULL; + } + p[0] = 0xdead1234; + nn = nn / sizeof (long) - 1; + p[1] = n; + p[nn] = 0xdead5678; +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "malloc\t%d\t%p\t%s:%d\n", n, &p[2], file, line); +#endif + return (void *) &p[2]; +} + +static void * +xrealloc_(void *old, int n, char *file, int line) +{ + int nn = n + 4 * sizeof (long), nnn; + long *p, *pp; + + if (n == 0 || !old) { + return xmalloc_(n, file, line); + } + p = &((long *) old)[-2]; + if (p[0] != 0xdead1234) { + fprintf(stderr, "*** low end corruption @ %p\n", old); + abort(); + } + nnn = p[1] + 4 * sizeof (long); + nnn = nnn / sizeof (long) - 1; + if (p[nnn] != 0xdead5678) { + fprintf(stderr, "*** high end corruption @ %p\n", old); + abort(); + } + pp = realloc(p, nn); + if (!pp) { +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "realloc\t%p,%d\tNULL\t%s:%d\n", old, n, file, line); +#endif + return NULL; + } +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "realloc\t%p,%d\t%p\t%s:%d\n", old, n, &pp[2], file, line); +#endif + p = pp; + p[1] = n; + nn = nn / sizeof (long) - 1; + p[nn] = 0xdead5678; + return (void *) &p[2]; +} + +static void +xfree_(void *x, char *file, int line) +{ + long *p; + int n; + + if (!x) { + return; + } + p = &((long *) x)[-2]; + if (p[0] != 0xdead1234) { + fprintf(stderr, "*** low end corruption @ %p\n", x); + abort(); + } + n = p[1] + 4 * sizeof (long); + n = n / sizeof (long) - 1; + if (p[n] != 0xdead5678) { + fprintf(stderr, "*** high end corruption @ %p\n", x); + abort(); + } +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "free\t%p\t\t%s:%d\n", x, file, line); +#endif + free(p); +} + +static void +xfree__(void *x) +{ + xfree_(x, "unknown location", 0); +} + +static char * +xstrdup_(const char *str, char *file, int line) +{ + char *p; + + if (!str) { +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "strdup\tNULL\tNULL\t%s:%d\n", file, line); +#endif + return NULL; + } + p = xmalloc_(strlen(str) + 1, file, line); + if (p) { + strcpy(p, str); + } +#if (MEMORY_DEBUG > 1) + fprintf(stderr, "strdup\t%p\t%p\t%s:%d\n", str, p, file, line); +#endif + return p; +} + +#define xmalloc(x) xmalloc_(x, __FILE__, __LINE__) +#define xrealloc(x,y) xrealloc_(x, y, __FILE__, __LINE__) +#define xfree(x) xfree_(x, __FILE__, __LINE__) +#define xstrdup(x) xstrdup_(x, __FILE__, __LINE__) + +#else + +#define xmalloc(x) sqlite4_malloc(0, x) +#define xrealloc(x,y) sqlite4_realloc(0, x, y) +#define xfree(x) sqlite4_free(0, x) +#define xstrdup(x) strdup_(x) + +#endif + +#if defined(_WIN32) || defined(_WIN64) + +#define vsnprintf _vsnprintf +#define snprintf _snprintf +#define strcasecmp _stricmp +#define strncasecmp _strnicmp + +static HINSTANCE NEAR hModule; /* Saved module handle for resources */ + +#endif + +#undef strncasecmp +#define strncasecmp(A,B,C) sqlite4_strnicmp(A,B,C) +#undef strcasecmp +#define strcasecmp(A,B) strcasecmp_(A,B) + +#if defined(__GNUC__) && (__GNUC__ >= 2) +static int strcasecmp_(const char *a, const char *b) + __attribute__((__unused__)); +#endif + +static int strcasecmp_(const char *a, const char *b) +{ + int c = strlen(a), d = strlen(b); + + if (c > d) { + return strncasecmp(a, b, c); + } + return strncasecmp(a, b, d); +} + +#if defined(_WIN32) || defined(_WIN64) + +/* + * SQLHENV, SQLHDBC, and SQLHSTMT synchronization + * is done using a critical section in ENV structure. + */ + +#define HDBC_LOCK(hdbc) \ +{ \ + DBC *d; \ + \ + if ((hdbc) == SQL_NULL_HDBC) { \ + return SQL_INVALID_HANDLE; \ + } \ + d = (DBC *) (hdbc); \ + if (d->magic != DBC_MAGIC || !d->env) { \ + return SQL_INVALID_HANDLE; \ + } \ + if (d->env->magic != ENV_MAGIC) { \ + return SQL_INVALID_HANDLE; \ + } \ + EnterCriticalSection(&d->env->cs); \ + d->env->owner = GetCurrentThreadId(); \ +} + +#define HDBC_UNLOCK(hdbc) \ + if ((hdbc) != SQL_NULL_HDBC) { \ + DBC *d; \ + \ + d = (DBC *) (hdbc); \ + if (d->magic == DBC_MAGIC && d->env && \ + d->env->magic == ENV_MAGIC) { \ + d->env->owner = 0; \ + LeaveCriticalSection(&d->env->cs); \ + } \ + } + +#define HSTMT_LOCK(hstmt) \ +{ \ + DBC *d; \ + \ + if ((hstmt) == SQL_NULL_HSTMT) { \ + return SQL_INVALID_HANDLE; \ + } \ + d = (DBC *) ((STMT *) (hstmt))->dbc; \ + if (d->magic != DBC_MAGIC || !d->env) { \ + return SQL_INVALID_HANDLE; \ + } \ + if (d->env->magic != ENV_MAGIC) { \ + return SQL_INVALID_HANDLE; \ + } \ + EnterCriticalSection(&d->env->cs); \ + d->env->owner = GetCurrentThreadId(); \ +} + +#define HSTMT_UNLOCK(hstmt) \ + if ((hstmt) != SQL_NULL_HSTMT) { \ + DBC *d; \ + \ + d = (DBC *) ((STMT *) (hstmt))->dbc; \ + if (d->magic == DBC_MAGIC && d->env && \ + d->env->magic == ENV_MAGIC) { \ + d->env->owner = 0; \ + LeaveCriticalSection(&d->env->cs); \ + } \ + } + +#else + +/* + * On UN*X assume that we are single-threaded or + * the driver manager provides serialization for us. + * + * In iODBC (3.52.x) serialization can be turned + * on using the DSN property "ThreadManager=yes". + * + * In unixODBC that property is named + * "Threading=0-3" and takes one of these values: + * + * 0 - no protection + * 1 - statement level protection + * 2 - connection level protection + * 3 - environment level protection + * + * unixODBC 2.2.11 uses environment level protection + * by default when it has been built with pthread + * support. + */ + +#define HDBC_LOCK(hdbc) +#define HDBC_UNLOCK(hdbc) +#define HSTMT_LOCK(hdbc) +#define HSTMT_UNLOCK(hdbc) + +#endif + +/* + * tolower() replacement w/o locale + */ + +static const char upper_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const char lower_chars[] = "abcdefghijklmnopqrstuvwxyz"; + +static int +TOLOWER(int c) +{ + if (c) { + char *p = strchr(upper_chars, c); + + if (p) { + c = lower_chars[p - upper_chars]; + } + } + return c; +} + +/* + * isdigit() replacement w/o ctype.h + */ + +static const char digit_chars[] = "0123456789"; + +#define ISDIGIT(c) \ + ((c) && strchr(digit_chars, (c)) != NULL) + +/* + * isspace() replacement w/o ctype.h + */ + +static const char space_chars[] = " \f\n\r\t\v"; + +#define ISSPACE(c) \ + ((c) && strchr(space_chars, (c)) != NULL) + + +/* + * Forward declarations of static functions. + */ + +static void dbtraceapi(DBC *d, char *fn, const char *sql); +static void freedyncols(STMT *s); +static void freeresult(STMT *s, int clrcols); +static void freerows(char **rowp); +static void unbindcols(STMT *s); +static void s4stmt_drop(STMT *s); + +static SQLRETURN drvexecute(SQLHSTMT stmt, int initial); +static SQLRETURN freestmt(HSTMT stmt); +static SQLRETURN mkbindcols(STMT *s, int ncols); +static SQLRETURN setupdyncols(STMT *s, sqlite4_stmt *s4stmt, int *ncolsp); +static SQLRETURN setupparbuf(STMT *s, BINDPARM *p); +static SQLRETURN starttran(STMT *s); +static SQLRETURN setupparam(STMT *s, char *sql, int pnum); +static SQLRETURN getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, + SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, + int partial); + +#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) +/* MS Access hack part 1 (reserved error -7748) */ +static COL *statSpec2P, *statSpec3P; +#endif + +#if (MEMORY_DEBUG < 1) +/** + * Duplicate string using xmalloc(). + * @param str string to be duplicated + * @result pointer to new string or NULL + */ + +static char * +strdup_(const char *str) +{ + char *p = NULL; + + if (str) { + p = xmalloc(strlen(str) + 1); + if (p) { + strcpy(p, str); + } + } + return p; +} +#endif + +/** + * Append string to dynamic string. + * @param dsp dstr pointer + * @param str string to append + * @result dsp result dstr pointer or NULL. + */ + +static dstr * +dsappend(dstr *dsp, const char *str) +{ + int len; + + if (!str) { + return dsp; + } + len = strlen(str); + if (!dsp) { + int max = 256; + + if (max < len) { + max += len; + } + dsp = xmalloc(max); + if (dsp) { + dsp->max = max; + dsp->len = dsp->oom = 0; + goto copy; + } + return dsp; + } + if (dsp->oom) { + return dsp; + } + if (dsp->len + len > dsp->max) { + int max = dsp->max + len + 256; + dstr *ndsp = xrealloc(dsp, max); + + if (!ndsp) { + strcpy(dsp->buffer, "OUT OF MEMORY"); + dsp->max = dsp->len = 13; + dsp->oom = 1; + return dsp; + } + dsp = ndsp; + dsp->max = max; + } +copy: + strcpy(dsp->buffer + dsp->len, str); + dsp->len += len; + return dsp; +} + +/** + * Append string quoted to dynamic string. + * @param dsp dstr pointer + * @param str string to append + * @result dsp result dstr pointer or NULL. + */ + +static dstr * +dsappendq(dstr *dsp, const char *str) +{ + int len; + const char *p; + char *q; + + if (!str) { + return dsp; + } + len = strlen(str); + for (p = str; *p; ++p) { + if (p[0] == '"') { + ++len; + } + } + if (!dsp) { + int max = 256; + + if (max < len) { + max += len; + } + dsp = xmalloc(max); + if (dsp) { + dsp->max = max; + dsp->len = dsp->oom = 0; + goto copy; + } + return dsp; + } + if (dsp->oom) { + return dsp; + } + if (dsp->len + len > dsp->max) { + int max = dsp->max + len + 256; + dstr *ndsp = xrealloc(dsp, max); + + if (!ndsp) { + strcpy(dsp->buffer, "OUT OF MEMORY"); + dsp->max = dsp->len = 13; + dsp->oom = 1; + return dsp; + } + dsp = ndsp; + dsp->max = max; + } +copy: + for (p = str, q = dsp->buffer + dsp->len; *p; ++p) { + *q++ = *p; + if (p[0] == '"') { + *q++ = '"'; + } + } + *q = '\0'; + dsp->len += len; + return dsp; +} + +/** + * Return dynamic string's value. + * @param dsp dstr pointer + * @result string value + */ + +static const char * +dsval(dstr *dsp) +{ + if (dsp) { + return (const char *) dsp->buffer; + } + return "ERROR"; +} + +/** + * Check error on dynamic string. + * @param dsp dstr pointer + * @result true when error pending + */ + +static int +dserr(dstr *dsp) +{ + return !dsp || dsp->oom; +} + +/** + * Free dynamic string. + * @param dsp dstr pointer + */ + +static void +dsfree(dstr *dsp) +{ + if (dsp) { + xfree(dsp); + } +} + +#ifdef WCHARSUPPORT + +/** + * Return length of UNICODE string. + * @param str UNICODE string + * @result length of string in characters + */ + +static int +uc_strlen(SQLWCHAR *str) +{ + int len = 0; + + if (str) { + while (*str) { + ++len; + ++str; + } + } + return len; +} + +/** + * Copy UNICODE string like strncpy(). + * @param dest destination area + * @param src source area + * @param len length of source area in characters + * @return pointer to destination area + */ + +static SQLWCHAR * +uc_strncpy(SQLWCHAR *dest, SQLWCHAR *src, int len) +{ + int i = 0; + + while (i < len) { + if (!src[i]) { + break; + } + dest[i] = src[i]; + ++i; + } + if (i < len) { + dest[i] = 0; + } + return dest; +} + +/** + * Make UNICODE string from UTF8 string into buffer. + * @param str UTF8 string to be converted + * @param len length in characters of str or -1 + * @param uc destination area to receive UNICODE string + * @param ucLen byte length of destination area + */ + +static void +uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen) +{ + ucLen = ucLen / sizeof (SQLWCHAR); + if (!uc || ucLen < 0) { + return; + } + if (len < 0) { + len = ucLen * 5; + } + uc[0] = 0; + if (str) { + int i = 0; + + while (i < len && *str && i < ucLen) { + unsigned char c = str[0]; + + if (c < 0x80) { + uc[i++] = c; + ++str; + } else if (c <= 0xc1 || c >= 0xf5) { + /* illegal, ignored */ + ++str; + } else if (c < 0xe0) { + if ((str[1] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x1f) << 6) | (str[1] & 0x3f); + + uc[i++] = t; + str += 2; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf0) { + if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x0f) << 12) | + ((str[1] & 0x3f) << 6) | (str[2] & 0x3f); + + uc[i++] = t; + str += 3; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xf8) { + if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && + (str[3] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x03) << 18) | + ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | + (str[3] & 0x3f); + + if (sizeof (SQLWCHAR) == 2 * sizeof (char) && + t >= 0x10000) { + t -= 0x10000; + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); + if (i >= ucLen) { + break; + } + t = 0xdc00 | (t & 0x3ff); + } + uc[i++] = t; + str += 4; + } else { + uc[i++] = c; + ++str; + } + } else if (c < 0xfc) { + if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && + (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80) { + unsigned long t = ((c & 0x01) << 24) | + ((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) | + ((str[3] & 0x3f) << 6) | (str[4] & 0x3f); + + if (sizeof (SQLWCHAR) == 2 * sizeof (char) && + t >= 0x10000) { + t -= 0x10000; + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); + if (i >= ucLen) { + break; + } + t = 0xdc00 | (t & 0x3ff); + } + uc[i++] = t; + str += 5; + } else { + uc[i++] = c; + ++str; + } + } else { + /* ignore */ + ++str; + } + } + if (i < ucLen) { + uc[i] = 0; + } + } +} + +/** + * Make UNICODE string from UTF8 string. + * @param str UTF8 string to be converted + * @param len length of UTF8 string + * @return alloc'ed UNICODE string to be free'd by uc_free() + */ + +static SQLWCHAR * +uc_from_utf(unsigned char *str, int len) +{ + SQLWCHAR *uc = NULL; + int ucLen; + + if (str) { + if (len == SQL_NTS) { + len = strlen((char *) str); + } + ucLen = sizeof (SQLWCHAR) * (len + 1); + uc = xmalloc(ucLen); + if (uc) { + uc_from_utf_buf(str, len, uc, ucLen); + } + } + return uc; +} + +/** + * Make UTF8 string from UNICODE string. + * @param str UNICODE string to be converted + * @param len length of UNICODE string in bytes + * @return alloc'ed UTF8 string to be free'd by uc_free() + */ + +static char * +uc_to_utf(SQLWCHAR *str, int len) +{ + int i; + char *cp, *ret = NULL; + + if (!str) { + return ret; + } + if (len == SQL_NTS) { + len = uc_strlen(str); + } else { + len = len / sizeof (SQLWCHAR); + } + cp = xmalloc(len * 6 + 1); + if (!cp) { + return ret; + } + ret = cp; + for (i = 0; i < len; i++) { + unsigned long c = str[i]; + + if (sizeof (SQLWCHAR) == 2 * sizeof (char)) { + c &= 0xffff; + } + if (c < 0x80) { + *cp++ = c; + } else if (c < 0x800) { + *cp++ = 0xc0 | ((c >> 6) & 0x1f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + if (sizeof (SQLWCHAR) == 2 * sizeof (char) && + c >= 0xd800 && c <= 0xdbff && i + 1 < len) { + unsigned long c2 = str[i + 1] & 0xffff; + + if (c2 >= 0xdc00 && c2 <= 0xdfff) { + c = (((c & 0x3ff) << 10) | (c2 & 0x3ff)) + 0x10000; + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + ++i; + continue; + } + } + *cp++ = 0xe0 | ((c >> 12) & 0x0f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x200000) { + *cp++ = 0xf0 | ((c >> 18) & 0x07); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x4000000) { + *cp++ = 0xf8 | ((c >> 24) & 0x03); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } else if (c < 0x80000000) { + *cp++ = 0xfc | ((c >> 31) & 0x01); + *cp++ = 0x80 | ((c >> 24) & 0x3f); + *cp++ = 0x80 | ((c >> 18) & 0x3f); + *cp++ = 0x80 | ((c >> 12) & 0x3f); + *cp++ = 0x80 | ((c >> 6) & 0x3f); + *cp++ = 0x80 | (c & 0x3f); + } + } + *cp = '\0'; + return ret; +} + +#endif + +#ifdef WINTERFACE + +/** + * Make UTF8 string from UNICODE string. + * @param str UNICODE string to be converted + * @param len length of UNICODE string in characters + * @return alloc'ed UTF8 string to be free'd by uc_free() + */ + +static char * +uc_to_utf_c(SQLWCHAR *str, int len) +{ + if (len != SQL_NTS) { + len = len * sizeof (SQLWCHAR); + } + return uc_to_utf(str, len); +} + +#endif + +#if defined(WCHARSUPPORT) || defined(_WIN32) || defined(_WIN64) + +/** + * Free converted UTF8 or UNICODE string. + * @param str string to be free'd + */ + +static void +uc_free(void *str) +{ + if (str) { + xfree(str); + } +} + +#endif + +#if defined(_WIN32) || defined(_WIN64) + +/** + * Convert multibyte, current code page string to UTF8 string, + * @param str multibyte string to be converted + * @param len length of multibyte string + * @return alloc'ed UTF8 string to be free'd by uc_free() + */ + +static char * +wmb_to_utf(char *str, int len) +{ + WCHAR *wstr; + OSVERSIONINFO ovi; + int nchar, is2k, cp = CP_OEMCP; + + ovi.dwOSVersionInfoSize = sizeof (ovi); + GetVersionEx(&ovi); + is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; + if (AreFileApisANSI()) { + cp = is2k ? CP_THREAD_ACP : CP_ACP; + } + nchar = MultiByteToWideChar(cp, 0, str, len, NULL, 0); + wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); + if (!wstr) { + return NULL; + } + wstr[0] = 0; + nchar = MultiByteToWideChar(cp, 0, str, len, wstr, nchar); + wstr[nchar] = 0; + str = xmalloc((nchar + 1) * 7); + if (!str) { + xfree(wstr); + return NULL; + } + str[0] = '\0'; + nchar = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, nchar * 7, 0, 0); + str[nchar] = '\0'; + xfree(wstr); + return str; +} + +#ifndef WINTERFACE + +/** + * Convert multibyte, current code page string to UTF8 string, + * @param str multibyte string to be converted + * @param len length of multibyte string + * @return alloc'ed UTF8 string to be free'd by uc_free() + */ + +static char * +wmb_to_utf_c(char *str, int len) +{ + if (len == SQL_NTS) { + len = strlen(str); + } + return wmb_to_utf(str, len); +} + +#endif + +/** + * Convert UTF8 string to multibyte, current code page string, + * @param str UTF8 string to be converted + * @param len length of UTF8 string + * @return alloc'ed multibyte string to be free'd by uc_free() + */ + +static char * +utf_to_wmb(char *str, int len) +{ + WCHAR *wstr; + OSVERSIONINFO ovi; + int nchar, is2k, cp = CP_OEMCP; + + ovi.dwOSVersionInfoSize = sizeof (ovi); + GetVersionEx(&ovi); + is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; + if (AreFileApisANSI()) { + cp = is2k ? CP_THREAD_ACP : CP_ACP; + } + nchar = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); + wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); + if (!wstr) { + return NULL; + } + wstr[0] = 0; + nchar = MultiByteToWideChar(CP_UTF8, 0, str, len, wstr, nchar); + wstr[nchar] = 0; + str = xmalloc((nchar + 1) * 7); + if (!str) { + xfree(wstr); + return NULL; + } + str[0] = '\0'; + nchar = WideCharToMultiByte(cp, 0, wstr, -1, str, nchar * 7, 0, 0); + str[nchar] = '\0'; + xfree(wstr); + return str; +} + +#ifdef WINTERFACE + +/** + * Convert multibyte, current code page string to UNICODE string, + * @param str multibyte string to be converted + * @param len length of multibyte string + * @return alloc'ed UNICODE string to be free'd by uc_free() + */ + +static WCHAR * +wmb_to_uc(char *str, int len) +{ + WCHAR *wstr; + OSVERSIONINFO ovi; + int nchar, is2k, cp = CP_OEMCP; + + ovi.dwOSVersionInfoSize = sizeof (ovi); + GetVersionEx(&ovi); + is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; + if (AreFileApisANSI()) { + cp = is2k ? CP_THREAD_ACP : CP_ACP; + } + nchar = MultiByteToWideChar(cp, 0, str, len, NULL, 0); + wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); + if (!wstr) { + return NULL; + } + wstr[0] = 0; + nchar = MultiByteToWideChar(cp, 0, str, len, wstr, nchar); + wstr[nchar] = 0; + return wstr; +} + +/** + * Convert UNICODE string to multibyte, current code page string, + * @param str UNICODE string to be converted + * @param len length of UNICODE string + * @return alloc'ed multibyte string to be free'd by uc_free() + */ + +static char * +uc_to_wmb(WCHAR *wstr, int len) +{ + char *str; + OSVERSIONINFO ovi; + int nchar, is2k, cp = CP_OEMCP; + + ovi.dwOSVersionInfoSize = sizeof (ovi); + GetVersionEx(&ovi); + is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; + if (AreFileApisANSI()) { + cp = is2k ? CP_THREAD_ACP : CP_ACP; + } + nchar = WideCharToMultiByte(cp, 0, wstr, len, NULL, 0, 0, 0); + str = xmalloc((nchar + 1) * 2); + if (!str) { + return NULL; + } + str[0] = '\0'; + nchar = WideCharToMultiByte(cp, 0, wstr, len, str, nchar * 2, 0, 0); + str[nchar] = '\0'; + return str; +} + +#endif /* WINTERFACE */ + +#endif /* _WIN32 || _WIN64 */ + + +#ifdef USE_DLOPEN_FOR_GPPS + +#include <dlfcn.h> + +#define SQLGetPrivateProfileString(A,B,C,D,E,F) drvgpps(d,A,B,C,D,E,F) + +/* + * EXPERIMENTAL: SQLGetPrivateProfileString infrastructure using + * dlopen(), in theory this makes the driver independent from the + * driver manager, i.e. the same driver binary can run with iODBC + * and unixODBC. + */ + +static void +drvgetgpps(DBC *d) +{ + void *lib; + int (*gpps)(); + + lib = dlopen("libodbcinst.so.1", RTLD_LAZY); + if (!lib) { + lib = dlopen("libodbcinst.so", RTLD_LAZY); + } + if (!lib) { + lib = dlopen("libiodbcinst.so.2", RTLD_LAZY); + } + if (!lib) { + lib = dlopen("libiodbcinst.so", RTLD_LAZY); + } + if (lib) { + gpps = (int (*)()) dlsym(lib, "SQLGetPrivateProfileString"); + if (!gpps) { + dlclose(lib); + return; + } + d->instlib = lib; + d->gpps = gpps; + } +} + +static void +drvrelgpps(DBC *d) +{ + if (d->instlib) { + dlclose(d->instlib); + d->instlib = 0; + } +} + +static int +drvgpps(DBC *d, char *sect, char *ent, char *def, char *buf, + int bufsiz, char *fname) +{ + if (d->gpps) { + return d->gpps(sect, ent, def, buf, bufsiz, fname); + } + strncpy(buf, def, bufsiz); + buf[bufsiz - 1] = '\0'; + return 1; +} +#else +#include <odbcinst.h> +#define drvgetgpps(d) +#define drvrelgpps(d) +#endif + +/* + * Internal function to bind sqlite4 parameters. + */ + +static void +s4bind(DBC *d, sqlite4_stmt *stmt, int nparams, BINDPARM *p) +{ + int i; + + if (stmt && p && nparams > 0) { + for (i = 0; i < nparams; i++, p++) { + switch (p->s4type) { + default: + case SQLITE4_NULL: + sqlite4_bind_null(stmt, i + 1); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: NULL\n", i + 1); + fflush(d->trace); + } + break; + case SQLITE4_TEXT: + sqlite4_bind_text(stmt, i + 1, p->s4val, p->s4size, + SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%*s'\n", i + 1, + p->s4size, (char *) p->s4val); + fflush(d->trace); + } + break; + case SQLITE4_BLOB: + sqlite4_bind_blob(stmt, i + 1, p->s4val, p->s4size, + SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: [BLOB]'\n", i + 1); + fflush(d->trace); + } + break; + case SQLITE4_FLOAT: + sqlite4_bind_double(stmt, i + 1, p->s4dval); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", + i + 1, p->s4dval); + fflush(d->trace); + } + break; + case SQLITE4_INTEGER: + if (p->s4size > sizeof (int)) { + sqlite4_bind_int64(stmt, i + 1, p->s4lival); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter %d: %I64d\n", +#else + "-- parameter %d: %lld\n", +#endif + i + 1, p->s4lival); + fflush(d->trace); + } + } else { + sqlite4_bind_int(stmt, i + 1, p->s4ival); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", + i + 1, p->s4ival); + fflush(d->trace); + } + } + break; + } + } + } +} + +/** + * @typedef TBLRES + * @struct tblres + * Internal structure for managing driver's + * sqlite4_get_table() implementation. + */ + +typedef struct tblres { + char **resarr; /**< result array */ + char *errmsg; /**< error message or NULL */ + sqlite4_stmt *stmt; /**< sqlite4 statement pointer */ + STMT *s; /**< Driver statement pointer */ + int nalloc; /**< alloc'ed size of result array */ + int nrow; /**< number of rows in result array */ + int ncol; /**< number of columns in result array */ + PTRDIFF_T ndata; /**< index into result array */ + int rc; /**< SQLite return code */ +} TBLRES; + +/* + * Driver's version of sqlite4_get_table() and friends which are + * capable of dealing with blobs. + */ + +static int +drvgettable_row(TBLRES *t, int ncol, int rc) +{ + int need; + int i; + char *p; + + if (t->nrow == 0 && rc == SQLITE4_ROW) { + need = ncol * 2; + } else { + need = ncol; + } + if (t->ndata + need >= t->nalloc) { + char **resnew; + int nalloc = t->nalloc * 2 + need + 1; + + resnew = xrealloc(t->resarr, sizeof (char *) * nalloc); + if (!resnew) { +nomem: + t->rc = SQLITE4_NOMEM; + return 1; + } + t->nalloc = nalloc; + t->resarr = resnew; + } + /* column names when first row */ + if (t->nrow == 0) { + t->ncol = ncol; + for (i = 0; i < ncol; i++) { + p = (char *) sqlite4_column_name(t->stmt, i); + if (p) { + char *q = xmalloc(strlen(p) + 1); + + if (!q) { + goto nomem; + } + strcpy(q, p); + p = q; + } + t->resarr[t->ndata++] = p; + } + if (t->s && t->s->guessed_types) { + int ncol2 = ncol; + + setupdyncols(t->s, t->stmt, &ncol2); + t->s->guessed_types = 0; + t->s->ncols = ncol; + } + } else if (t->ncol != ncol) { + t->errmsg = sqlite4_mprintf(0, "drvgettable() called with two or" + " more incompatible queries"); + t->rc = SQLITE4_ERROR; + return 1; + } + /* copy row data */ + if (rc == SQLITE4_ROW) { + for (i = 0; i < ncol; i++) { + int coltype = sqlite4_column_type(t->stmt, i); + + p = NULL; + if (coltype == SQLITE4_BLOB) { + int k, nbytes; + char *qp; + unsigned const char *bp; + + bp = sqlite4_column_blob(t->stmt, i, &nbytes); + qp = xmalloc(nbytes * 2 + 4); + if (!qp) { + goto nomem; + } + p = qp; + *qp++ = 'X'; + *qp++ = '\''; + for (k = 0; k < nbytes; k++) { + *qp++ = xdigits[(bp[k] >> 4)]; + *qp++ = xdigits[(bp[k] & 0xF)]; + } + *qp++ = '\''; + *qp = '\0'; + } else if (coltype != SQLITE4_NULL) { + int nbytes; + + p = xstrdup((char *) sqlite4_column_text(t->stmt, i, &nbytes)); + if (!p) { + goto nomem; + } + } + t->resarr[t->ndata++] = p; + } + t->nrow++; + } + return 0; +} + +static int +drvgettable(STMT *s, const char *sql, char ***resp, int *nrowp, + int *ncolp, char **errp, int nparam, BINDPARM *p) +{ + DBC *d = (DBC *) s->dbc; + int rc = SQLITE4_OK, keep = sql == NULL; + TBLRES tres; + int sqlleft = 0; + int nretry = 0, haveerr = 0; + + if (!resp) { + return SQLITE4_ERROR; + } + *resp = NULL; + if (nrowp) { + *nrowp = 0; + } + if (ncolp) { + *ncolp = 0; + } + tres.errmsg = NULL; + tres.nrow = 0; + tres.ncol = 0; + tres.ndata = 1; + tres.nalloc = 20; + tres.rc = SQLITE4_OK; + tres.resarr = xmalloc(sizeof (char *) * tres.nalloc); + tres.stmt = NULL; + tres.s = s; + if (!tres.resarr) { + return SQLITE4_NOMEM; + } + tres.resarr[0] = 0; + if (sql == NULL) { + tres.stmt = s->s4stmt; + if (tres.stmt == NULL) { + return SQLITE4_NOMEM; + } + goto retrieve; + } + while (sql && *sql && (rc == SQLITE4_OK || + (rc == SQLITE4_SCHEMA && (++nretry) < 2))) { + int ncol; + + tres.stmt = NULL; + dbtraceapi(d, "sqlite4_prepare", sql); + rc = sqlite4_prepare(d->sqlite, sql, -1, &tres.stmt, &sqlleft); + if (rc != SQLITE4_OK) { + if (tres.stmt) { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(tres.stmt); + tres.stmt = NULL; + } + continue; + } + if (!tres.stmt) { + /* this happens for a comment or white-space */ + sql += sqlleft; + continue; + } +retrieve: + if (sqlite4_bind_parameter_count(tres.stmt) != nparam) { + if (errp) { + *errp = + sqlite4_mprintf(0, "%s", + "parameter marker count incorrect"); + } + haveerr = 1; + rc = SQLITE4_ERROR; + goto tbldone; + } + s4bind(d, tres.stmt, nparam, p); + ncol = sqlite4_column_count(tres.stmt); + while (1) { + if (s->max_rows && tres.nrow >= s->max_rows) { + rc = SQLITE4_OK; + break; + } + rc = sqlite4_step(tres.stmt); + if (rc == SQLITE4_ROW || rc == SQLITE4_DONE) { + if (drvgettable_row(&tres, ncol, rc)) { + rc = SQLITE4_ABORT; + goto tbldone; + } + } + if (rc != SQLITE4_ROW) { + if (keep) { + dbtraceapi(d, "sqlite4_reset", 0); + rc = sqlite4_reset(tres.stmt); + s->s4stmt_noreset = 1; + } else { + dbtraceapi(d, "sqlite4_finalize", 0); + rc = sqlite4_finalize(tres.stmt); + } + tres.stmt = 0; + if (rc != SQLITE4_SCHEMA) { + nretry = 0; + sql += sqlleft; + while (sql && ISSPACE(*sql)) { + sql++; + } + } + if (rc == SQLITE4_DONE) { + rc = SQLITE4_OK; + } + break; + } + } + } +tbldone: + if (tres.stmt) { + if (keep) { + if (!s->s4stmt_noreset) { + dbtraceapi(d, "sqlite4_reset", 0); + sqlite4_reset(tres.stmt); + s->s4stmt_noreset = 1; + } + } else { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(tres.stmt); + } + } + if (haveerr) { + /* message already in *errp if any */ + } else if (rc != SQLITE4_OK && rc == sqlite4_errcode(d->sqlite) && errp) { + *errp = sqlite4_mprintf(0, "%s", sqlite4_errmsg(d->sqlite)); + } else if (errp) { + *errp = NULL; + } + if (tres.resarr) { + tres.resarr[0] = (char *) (tres.ndata - 1); + } + if (rc == SQLITE4_ABORT) { + freerows(&tres.resarr[1]); + if (tres.errmsg) { + if (errp) { + if (*errp) { + sqlite4_free(0, *errp); + } + *errp = tres.errmsg; + } else { + sqlite4_free(0, tres.errmsg); + } + } + return tres.rc; + } + sqlite4_free(0, tres.errmsg); + if (rc != SQLITE4_OK) { + freerows(&tres.resarr[1]); + return rc; + } + *resp = &tres.resarr[1]; + if (ncolp) { + *ncolp = tres.ncol; + } + if (nrowp) { + *nrowp = tres.nrow; + } + return rc; +} + +static int +sqlite4_get_table(sqlite4 *db, const char *sql, char ***resp, int *nrowp, + int *ncolp, char **errp) +{ + int rc = SQLITE4_OK; + TBLRES tres; + int sqlleft = 0; + int nretry = 0, haveerr = 0; + + if (!resp) { + return SQLITE4_ERROR; + } + *resp = NULL; + if (nrowp) { + *nrowp = 0; + } + if (ncolp) { + *ncolp = 0; + } + tres.errmsg = NULL; + tres.nrow = 0; + tres.ncol = 0; + tres.ndata = 1; + tres.nalloc = 20; + tres.rc = SQLITE4_OK; + tres.resarr = xmalloc(sizeof (char *) * tres.nalloc); + tres.stmt = NULL; + tres.s = 0; + if (!tres.resarr) { + return SQLITE4_NOMEM; + } + tres.resarr[0] = 0; + while (sql && *sql && (rc == SQLITE4_OK || + (rc == SQLITE4_SCHEMA && (++nretry) < 2))) { + int ncol; + + tres.stmt = NULL; + rc = sqlite4_prepare(db, sql, -1, &tres.stmt, &sqlleft); + if (rc != SQLITE4_OK) { + if (tres.stmt) { + sqlite4_finalize(tres.stmt); + tres.stmt = NULL; + } + continue; + } + if (!tres.stmt) { + /* this happens for a comment or white-space */ + sql += sqlleft; + continue; + } + ncol = sqlite4_column_count(tres.stmt); + while (1) { + rc = sqlite4_step(tres.stmt); + if (rc == SQLITE4_ROW || rc == SQLITE4_DONE) { + if (drvgettable_row(&tres, ncol, rc)) { + rc = SQLITE4_ABORT; + goto tbldone; + } + } + if (rc != SQLITE4_ROW) { + rc = sqlite4_finalize(tres.stmt); + tres.stmt = 0; + if (rc != SQLITE4_SCHEMA) { + nretry = 0; + sql += sqlleft; + while (sql && ISSPACE(*sql)) { + sql++; + } + } + if (rc == SQLITE4_DONE) { + rc = SQLITE4_OK; + } + break; + } + } + } +tbldone: + if (tres.stmt) { + sqlite4_finalize(tres.stmt); + } + if (haveerr) { + /* message already in *errp if any */ + } else if (rc != SQLITE4_OK && rc == sqlite4_errcode(db) && errp) { + *errp = sqlite4_mprintf(0, "%s", sqlite4_errmsg(db)); + } else if (errp) { + *errp = NULL; + } + if (tres.resarr) { + tres.resarr[0] = (char *) (tres.ndata - 1); + } + if (rc == SQLITE4_ABORT) { + freerows(&tres.resarr[1]); + if (tres.errmsg) { + if (errp) { + if (*errp) { + sqlite4_free(0, *errp); + } + *errp = tres.errmsg; + } else { + sqlite4_free(0, tres.errmsg); + } + } + return tres.rc; + } + sqlite4_free(0, tres.errmsg); + if (rc != SQLITE4_OK) { + freerows(&tres.resarr[1]); + return rc; + } + *resp = &tres.resarr[1]; + if (ncolp) { + *ncolp = tres.ncol; + } + if (nrowp) { + *nrowp = tres.nrow; + } + return rc; +} + +/** + * Set error message and SQL state on DBC + * @param d database connection pointer + * @param naterr native error code + * @param msg error message + * @param st SQL state + */ + +#if defined(__GNUC__) && (__GNUC__ >= 2) +static void setstatd(DBC *, int, char *, char *, ...) + __attribute__((format (printf, 3, 5))); +#endif + +static void +setstatd(DBC *d, int naterr, char *msg, char *st, ...) +{ + va_list ap; + + if (!d) { + return; + } + d->naterr = naterr; + d->logmsg[0] = '\0'; + if (msg) { + int count; + + va_start(ap, st); + count = vsnprintf((char *) d->logmsg, sizeof (d->logmsg), msg, ap); + va_end(ap); + if (count < 0) { + d->logmsg[sizeof (d->logmsg) - 1] = '\0'; + } + } + if (!st) { + st = "?????"; + } + strncpy(d->sqlstate, st, 5); + d->sqlstate[5] = '\0'; +} + +/** + * Set error message and SQL state on statement + * @param s statement pointer + * @param naterr native error code + * @param msg error message + * @param st SQL state + */ + +#if defined(__GNUC__) && (__GNUC__ >= 2) +static void setstat(STMT *, int, char *, char *, ...) + __attribute__((format (printf, 3, 5))); +#endif + +static void +setstat(STMT *s, int naterr, char *msg, char *st, ...) +{ + va_list ap; + + if (!s) { + return; + } + s->naterr = naterr; + s->logmsg[0] = '\0'; + if (msg) { + int count; + + va_start(ap, st); + count = vsnprintf((char *) s->logmsg, sizeof (s->logmsg), msg, ap); + va_end(ap); + if (count < 0) { + s->logmsg[sizeof (s->logmsg) - 1] = '\0'; + } + } + if (!st) { + st = "?????"; + } + strncpy(s->sqlstate, st, 5); + s->sqlstate[5] = '\0'; +} + +/** + * Report IM001 (not implemented) SQL error code for HDBC. + * @param dbc database connection handle + * @result ODBC error code + */ + +static SQLRETURN +drvunimpldbc(HDBC dbc) +{ + DBC *d; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + setstatd(d, -1, "not supported", "IM001"); + return SQL_ERROR; +} + +/** + * Report IM001 (not implemented) SQL error code for HSTMT. + * @param stmt statement handle + * @result ODBC error code + */ + +static SQLRETURN +drvunimplstmt(HSTMT stmt) +{ + STMT *s; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + setstat(s, -1, "not supported", "IM001"); + return SQL_ERROR; +} + +/** + * Free memory given pointer to memory pointer. + * @param x pointer to pointer to memory to be free'd + */ + +static void +freep(void *x) +{ + if (x && ((char **) x)[0]) { + xfree(((char **) x)[0]); + ((char **) x)[0] = NULL; + } +} + +/** + * Report S1000 (out of memory) SQL error given STMT. + * @param s statement pointer + * @result ODBC error code + */ + +static SQLRETURN +nomem(STMT *s) +{ + setstat(s, -1, "out of memory", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; +} + +/** + * Report S1000 (not connected) SQL error given STMT. + * @param s statement pointer + * @result ODBC error code + */ + +static SQLRETURN +noconn(STMT *s) +{ + setstat(s, -1, "not connected", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; +} + +/** + * Internal locale neutral strtod function. + * @param data pointer to string + * @param endp pointer for ending character + * @result double value + */ + +#if defined(HAVE_LOCALECONV) || defined(_WIN32) || defined(_WIN64) + +static double +ln_strtod(const char *data, char **endp) +{ + static struct lconv *lc = 0; + char buf[128], *p, *end; + double value; + + if (!lc) { + lc = localeconv(); + } + if (lc && lc->decimal_point && lc->decimal_point[0] && + lc->decimal_point[0] != '.') { + strncpy(buf, data, sizeof (buf) - 1); + buf[sizeof (buf) - 1] = '\0'; + p = strchr(buf, '.'); + if (p) { + *p = lc->decimal_point[0]; + } + p = buf; + } else { + p = (char *) data; + } + value = strtod(p, &end); + end = (char *) data + (end - p); + if (endp) { + *endp = end; + } + return value; +} + +#else + +#define ln_strtod(A,B) strtod(A,B) + +#endif + +/** + * Strip quotes from quoted string in-place. + * @param str string + */ + +static char * +unquote(char *str) +{ + if (str) { + int len = strlen(str); + + if (len > 1) { + if ((str[0] == '\'' && str[len - 1] == '\'') || + (str[0] == '"' && str[len - 1] == '"') || + (str[0] == '[' && str[len - 1] == ']')) { + str[len - 1] = '\0'; + strcpy(str, str + 1); + } + } + } + return str; +} + +/** + * Unescape search pattern for e.g. table name in + * catalog functions. Replacements in string are done in-place. + * @param str string + * @result number of pattern characters in string or 0 + */ + +static int +unescpat(char *str) +{ + char *p, *q; + int count = 0; + + p = str; + while ((q = strchr(p, '_')) != NULL) { + if (q == str || q[-1] != '\\') { + count++; + } + p = q + 1; + } + p = str; + while ((q = strchr(p, '%')) != NULL) { + if (q == str || q[-1] != '\\') { + count++; + } + p = q + 1; + } + p = str; + while ((q = strchr(p, '\\')) != NULL) { + if (q[1] == '\\' || q[1] == '_' || q[1] == '%') { + strcpy(q, q + 1); + } + p = q + 1; + } + return count; +} + +/** + * SQL LIKE string match with optional backslash escape handling. + * @param str string + * @param pat pattern + * @param esc when true, treat literally "\\" as "\", "\%" as "%", "\_" as "_" + * @result true when pattern matched + */ + +static int +namematch(char *str, char *pat, int esc) +{ + int cp, ch; + + while (1) { + cp = TOLOWER(*pat); + if (cp == '\0') { + if (*str != '\0') { + goto nomatch; + } + break; + } + if (*str == '\0' && cp != '%') { + goto nomatch; + } + if (cp == '%') { + while (*pat == '%') { + ++pat; + } + cp = TOLOWER(*pat); + if (cp == '\0') { + break; + } + while (1) { + if (cp != '_' && cp != '\\') { + while (*str) { + ch = TOLOWER(*str); + if (ch == cp) { + break; + } + ++str; + } + } + if (namematch(str, pat, esc)) { + goto match; + } + if (*str == '\0') { + goto nomatch; + } + ch = TOLOWER(*str); + ++str; + } + } + if (cp == '_') { + pat++; + str++; + continue; + } + if (esc && cp == '\\' && + (pat[1] == '\\' || pat[1] == '%' || pat[1] == '_')) { + ++pat; + cp = TOLOWER(*pat); + } + ch = TOLOWER(*str++); + ++pat; + if (ch != cp) { + goto nomatch; + } + } +match: + return 1; +nomatch: + return 0; +} + +/** + * Set SQLite options (PRAGMAs) given SQLite handle. + * @param x SQLite database handle + * @param d DBC pointer + * @result SQLite error code + * + * SQLite < 3.3.x and not shortnames DSN option: + * "full_column_names" is always turned on and "short_column_names" + * is always turned off, to get the table names in column labels. + */ + +static int +setsqliteopts(sqlite4 *x, DBC *d) +{ + int count = 0, step = 0, max, rc = SQLITE4_ERROR; + + max = d->longnames ? 3 : 1; + if (d->shortnames) { + max = 3; + } + while (step < max) { + if (step < 1) { + rc = sqlite4_exec(x, "PRAGMA empty_result_callbacks = on;", + NULL, NULL); + if (rc == SQLITE4_OK) { + rc = sqlite4_exec(x, d->fksupport ? + "PRAGMA foreign_keys = on;" : + "PRAGMA foreign_keys = off;", + NULL, NULL); + } + } else if (step < 2) { + rc = sqlite4_exec(x, d->shortnames ? + "PRAGMA full_column_names = off;" : + "PRAGMA full_column_names = on;", + NULL, NULL); + } else if (step < 3) { + rc = sqlite4_exec(x, d->shortnames ? + "PRAGMA short_column_names = on;" : + "PRAGMA short_column_names = off;", + NULL, NULL); + } + if (rc != SQLITE4_OK) { + return rc; + } + count = 0; + ++step; + } + return SQLITE4_OK; +} + +/** + * Free counted array of char pointers. + * @param rowp pointer to char pointer array + * + * The -1-th element of the array holds the array size. + * All non-NULL pointers of the array and then the array + * itself are free'd. + */ + +static void +freerows(char **rowp) +{ + PTRDIFF_T size, i; + + if (!rowp) { + return; + } + --rowp; + size = (PTRDIFF_T) rowp[0]; + for (i = 1; i <= size; i++) { + freep(&rowp[i]); + } + freep(&rowp); +} + +/** + * Map SQL field type from string to ODBC integer type code. + * @param typename field type string + * @param nosign pointer to indicator for unsigned field or NULL + * @param ov3 boolean, true for SQL_OV_ODBC3 + * @param nowchar boolean, for WINTERFACE don't use WCHAR + * @param dobigint boolean, force SQL_BIGINT on INTEGER columns + * @result SQL data type + */ + +static int +mapsqltype(const char *typename, int *nosign, int ov3, int nowchar, + int dobigint) +{ + char *p, *q; + int testsign = 0, result; + +#ifdef WINTERFACE + result = nowchar ? SQL_VARCHAR : SQL_WVARCHAR; +#else + result = SQL_VARCHAR; +#endif + if (!typename) { + return result; + } + q = p = xmalloc(strlen(typename) + 1); + if (!p) { + return result; + } + strcpy(p, typename); + while (*q) { + *q = TOLOWER(*q); + ++q; + } + if (strncmp(p, "inter", 5) == 0) { + } else if (strncmp(p, "int", 3) == 0 || + strncmp(p, "mediumint", 9) == 0) { + testsign = 1; + result = SQL_INTEGER; + } else if (strncmp(p, "numeric", 7) == 0) { + result = SQL_DOUBLE; + } else if (strncmp(p, "tinyint", 7) == 0) { + testsign = 1; + result = SQL_TINYINT; + } else if (strncmp(p, "smallint", 8) == 0) { + testsign = 1; + result = SQL_SMALLINT; + } else if (strncmp(p, "float", 5) == 0) { + result = SQL_DOUBLE; + } else if (strncmp(p, "double", 6) == 0 || + strncmp(p, "real", 4) == 0) { + result = SQL_DOUBLE; + } else if (strncmp(p, "timestamp", 9) == 0) { +#ifdef SQL_TYPE_TIMESTAMP + result = ov3 ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP; +#else + result = SQL_TIMESTAMP; +#endif + } else if (strncmp(p, "datetime", 8) == 0) { +#ifdef SQL_TYPE_TIMESTAMP + result = ov3 ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP; +#else + result = SQL_TIMESTAMP; +#endif + } else if (strncmp(p, "time", 4) == 0) { +#ifdef SQL_TYPE_TIME + result = ov3 ? SQL_TYPE_TIME : SQL_TIME; +#else + result = SQL_TIME; +#endif + } else if (strncmp(p, "date", 4) == 0) { +#ifdef SQL_TYPE_DATE + result = ov3 ? SQL_TYPE_DATE : SQL_DATE; +#else + result = SQL_DATE; +#endif +#ifdef SQL_LONGVARCHAR + } else if (strncmp(p, "text", 4) == 0 || + strncmp(p, "memo", 4) == 0 || + strncmp(p, "longvarchar", 11) == 0) { +#ifdef WINTERFACE + result = nowchar ? SQL_LONGVARCHAR : SQL_WLONGVARCHAR; +#else + result = SQL_LONGVARCHAR; +#endif +#ifdef WINTERFACE + } else if (strncmp(p, "wtext", 5) == 0 || + strncmp(p, "wvarchar", 8) == 0 || + strncmp(p, "longwvarchar", 12) == 0) { + result = SQL_WLONGVARCHAR; +#endif +#endif +#ifdef SQL_BIT + } else if (strncmp(p, "bool", 4) == 0 || + strncmp(p, "bit", 3) == 0) { + result = SQL_BIT; +#endif +#ifdef SQL_BIGINT + } else if (strncmp(p, "bigint", 6) == 0) { + testsign = 1; + result = SQL_BIGINT; +#endif + } else if (strncmp(p, "blob", 4) == 0) { + result = SQL_BINARY; + } else if (strncmp(p, "varbinary", 9) == 0) { + result = SQL_VARBINARY; + } else if (strncmp(p, "longvarbinary", 13) == 0) { + result = SQL_LONGVARBINARY; + } + if (nosign) { + if (testsign) { + *nosign = strstr(p, "unsigned") != NULL; + } else { + *nosign = 1; + } + } +#ifdef SQL_BIGINT + if (dobigint && result == SQL_INTEGER) { + result = SQL_BIGINT; + } +#endif + xfree(p); + return result; +} + +/** + * Get maximum display size and number of digits after decimal point + * from field type specification. + * @param typename field type specification + * @param sqltype target SQL data type + * @param mp pointer to maximum display size or NULL + * @param dp pointer to number of digits after decimal point or NULL + */ + +static void +getmd(const char *typename, int sqltype, int *mp, int *dp) +{ + int m = 0, d = 0; + + switch (sqltype) { + case SQL_INTEGER: m = 10; d = 9; break; + case SQL_TINYINT: m = 4; d = 3; break; + case SQL_SMALLINT: m = 6; d = 5; break; + case SQL_FLOAT: m = 25; d = 24; break; + case SQL_DOUBLE: m = 54; d = 53; break; + case SQL_VARCHAR: m = 255; d = 0; break; +#ifdef WINTERFACE +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: m = 255; d = 0; break; +#endif +#endif +#ifdef SQL_TYPE_DATE + case SQL_TYPE_DATE: +#endif + case SQL_DATE: m = 10; d = 0; break; +#ifdef SQL_TYPE_TIME + case SQL_TYPE_TIME: +#endif + case SQL_TIME: m = 8; d = 0; break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + case SQL_TIMESTAMP: m = 32; d = 3; break; +#ifdef SQL_LONGVARCHAR + case SQL_LONGVARCHAR : m = 65536; d = 0; break; +#endif +#ifdef WINTERFACE +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: m = 65536; d = 0; break; +#endif +#endif + case SQL_VARBINARY: m = 255; d = 0; break; + case SQL_LONGVARBINARY: m = 65536; d = 0; break; +#ifdef SQL_BIGINT + case SQL_BIGINT: m = 20; d = 19; break; +#endif +#ifdef SQL_BIT + case SQL_BIT: m = 1; d = 1; break; +#endif + } + if (m && typename) { + int mm, dd; + + if (sscanf(typename, "%*[^(](%d)", &mm) == 1) { + if (sqltype == SQL_TIMESTAMP) { + d = mm; + } +#ifdef SQL_TYPE_TIMESTAMP + else if (sqltype == SQL_TYPE_TIMESTAMP) { + d = mm; + } +#endif + else { + m = d = mm; + } + } else if (sscanf(typename, "%*[^(](%d,%d)", &mm, &dd) == 2) { + m = mm; + d = dd; + } + } + if (mp) { + *mp = m; + } + if (dp) { + *dp = d; + } +} + +/** + * Map SQL_C_DEFAULT to proper C type. + * @param type input C type + * @param stype input SQL type + * @param nosign 0=signed, 0>unsigned, 0<undefined + * @param nowchar when compiled with WINTERFACE don't use WCHAR + * @result C type + */ + +static int +mapdeftype(int type, int stype, int nosign, int nowchar) +{ + if (type == SQL_C_DEFAULT) { + switch (stype) { + case SQL_INTEGER: + type = (nosign > 0) ? SQL_C_ULONG : SQL_C_LONG; + break; + case SQL_TINYINT: + type = (nosign > 0) ? SQL_C_UTINYINT : SQL_C_TINYINT; + break; + case SQL_SMALLINT: + type = (nosign > 0) ? SQL_C_USHORT : SQL_C_SHORT; + break; + case SQL_FLOAT: + type = SQL_C_FLOAT; + break; + case SQL_DOUBLE: + type = SQL_C_DOUBLE; + break; + case SQL_TIMESTAMP: + type = SQL_C_TIMESTAMP; + break; + case SQL_TIME: + type = SQL_C_TIME; + break; + case SQL_DATE: + type = SQL_C_DATE; + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: + type = SQL_C_TYPE_TIMESTAMP; + break; +#endif +#ifdef SQL_C_TYPE_TIME + case SQL_TYPE_TIME: + type = SQL_C_TYPE_TIME; + break; +#endif +#ifdef SQL_C_TYPE_DATE + case SQL_TYPE_DATE: + type = SQL_C_TYPE_DATE; + break; +#endif +#ifdef WINTERFACE + case SQL_WVARCHAR: + case SQL_WCHAR: +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: +#endif + type = nowchar ? SQL_C_CHAR : SQL_C_WCHAR; + break; +#endif + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + type = SQL_C_BINARY; + break; +#ifdef SQL_BIT + case SQL_BIT: + type = SQL_C_BIT; + break; +#endif +#ifdef SQL_BIGINT + case SQL_BIGINT: + type = SQL_C_CHAR; + break; +#endif + default: +#ifdef WINTERFACE + type = nowchar ? SQL_C_CHAR : SQL_C_WCHAR; +#else + type = SQL_C_CHAR; +#endif + break; + } + } + return type; +} + +/** + * Fixup query string with optional parameter markers. + * @param sql original query string + * @param sqlLen length of query string or SQL_NTS + * @param nparam output number of parameters + * @param isselect output indicator for SELECT (1) or DDL statement (2) + * @param errmsg output error message + * @result newly allocated string containing query string for SQLite or NULL + */ + +static char * +fixupsql(char *sql, int sqlLen, int *nparam, int *isselect, char **errmsg) +{ + char *q = sql, *qz = NULL, *p, *inq = NULL, *out; + int np = 0, isddl = -1, size; + + if (errmsg) { + *errmsg = NULL; + } + if (sqlLen != SQL_NTS) { + qz = q = xmalloc(sqlLen + 1); + if (!qz) { + return NULL; + } + memcpy(q, sql, sqlLen); + q[sqlLen] = '\0'; + size = sqlLen * 4; + } else { + size = strlen(sql) * 4; + } + size += sizeof (char *) - 1; + size &= ~(sizeof (char *) - 1); + p = xmalloc(size); + if (!p) { +errout: + freep(&qz); + return NULL; + } + memset(p, 0, size); + out = p; + while (*q) { + switch (*q) { + case '\'': + case '\"': + if (q == inq) { + inq = NULL; + } else if (!inq) { + inq = q + 1; + + while (*inq) { + if (*inq == *q) { + if (inq[1] == *q) { + inq++; + } else { + break; + } + } + inq++; + } + } + *p++ = *q; + break; + case '?': + *p++ = *q; + if (!inq) { + np++; + } + break; + case ';': + if (!inq) { + if (isddl < 0) { + char *qq = out; + + while (*qq && ISSPACE(*qq)) { + ++qq; + } + if (*qq && *qq != ';') { + int i; + static const struct { + int len; + const char *str; + } ddlstr[] = { + { 5, "alter" }, + { 7, "analyze" }, + { 6, "attach" }, + { 5, "begin" }, + { 6, "commit" }, + { 6, "create" }, + { 6, "detach" }, + { 4, "drop" }, + { 3, "end" }, + { 7, "reindex" }, + { 7, "release" }, + { 8, "rollback" }, + { 9, "savepoint" }, + { 6, "vacuum" } + }; + + size = strlen(qq); + for (i = 0; i < array_size(ddlstr); i++) { + if (size >= ddlstr[i].len && + strncasecmp(qq, ddlstr[i].str, ddlstr[i].len) + == 0) { + isddl = 1; + break; + } + } + if (isddl != 1) { + isddl = 0; + } + } + } + if (isddl == 0) { + char *qq = q; + + do { + ++qq; + } while (*qq && ISSPACE(*qq)); + if (*qq && *qq != ';') { + freep(&out); + if (errmsg) { + *errmsg = "only one SQL statement allowed"; + } + goto errout; + } + } + } + *p++ = *q; + break; + case '{': + /* + * Deal with escape sequences: + * {d 'YYYY-MM-DD'}, {t ...}, {ts ...} + * {oj ...}, {fn ...} etc. + */ + if (!inq) { + int ojfn = 0, brc = 0; + char *inq2 = NULL, *end = q + 1, *start; + + while (*end && ISSPACE(*end)) { + ++end; + } + if (*end != 'd' && *end != 'D' && + *end != 't' && *end != 'T') { + ojfn = 1; + } + start = end; + while (*end) { + if (inq2 && *end == *inq2) { + inq2 = NULL; + } else if (inq2 == NULL && *end == '{') { + char *nerr = 0, *nsql; + + nsql = fixupsql(end, SQL_NTS, 0, 0, &nerr); + if (nsql && !nerr) { + strcpy(end, nsql); + } else { + brc++; + } + freep(&nsql); + } else if (inq2 == NULL && *end == '}') { + if (brc-- <= 0) { + break; + } + } else if (inq2 == NULL && (*end == '\'' || *end == '"')) { + inq2 = end; + } else if (inq2 == NULL && *end == '?') { + np++; + } + ++end; + } + if (*end == '}') { + char *end2 = end - 1; + + if (ojfn) { + while (start < end) { + if (ISSPACE(*start)) { + break; + } + ++start; + } + while (start < end) { + *p++ = *start; + ++start; + } + q = end; + break; + } else { + while (start < end2 && *start != '\'') { + ++start; + } + while (end2 > start && *end2 != '\'') { + --end2; + } + if (*start == '\'' && *end2 == '\'') { + while (start <= end2) { + *p++ = *start; + ++start; + } + q = end; + break; + } + } + } + } + /* FALL THROUGH */ + default: + *p++ = *q; + } + ++q; + } + freep(&qz); + *p = '\0'; + if (nparam) { + *nparam = np; + } + if (isselect) { + if (isddl > 0) { + *isselect = 2; + } else { + int incom = 0; + + p = out; + while (*p) { + switch (*p) { + case '-': + if (!incom && p[1] == '-') { + incom = -1; + } + break; + case '\n': + if (incom < 0) { + incom = 0; + } + break; + case '/': + if (incom > 0 && p[-1] == '*') { + incom = 0; + p++; + continue; + } else if (!incom && p[1] == '*') { + incom = 1; + } + break; + } + if (!incom && !ISSPACE(*p)) { + break; + } + p++; + } + size = strlen(p); + if (size >= 6 && + (strncasecmp(p, "select", 6) == 0 || + strncasecmp(p, "pragma", 6) == 0)) { + *isselect = 1; + } else if (size >= 7 && + strncasecmp(p, "explain", 7) == 0) { + *isselect = 1; + } else { + *isselect = 0; + } + } + } + return out; +} + +/** + * Find column given name in string array. + * @param cols string array + * @param ncols number of strings + * @param name column name + * @result >= 0 on success, -1 on error + */ + +static int +findcol(char **cols, int ncols, char *name) +{ + int i; + + if (cols) { + for (i = 0; i < ncols; i++) { + if (strcmp(cols[i], name) == 0) { + return i; + } + } + } + return -1; +} + +/** + * Fixup column information for a running statement. + * @param s statement to get fresh column information + * @param d DBC pointer + * + * The column labels get the table names stripped + * when there's more than one column and all table + * names are identical. + * + * The "dyncols" field of STMT is filled with column + * information obtained by SQLite "PRAGMA table_info" + * for each column whose table name is known. If the + * types are already present as with SQLite 2.5.7 + * this information is used instead. + */ + +static void +fixupdyncols(STMT *s, DBC *d) +{ + int i, k; + + if (!s->dyncols) { + return; + } + /* fixup labels */ + if (!s->longnames) { + if (s->dcols > 1) { + char *table = s->dyncols[0].table; + + for (i = 1; table[0] && i < s->dcols; i++) { + if (strcmp(s->dyncols[i].table, table)) { + break; + } + } + if (i >= s->dcols) { + for (i = 0; i < s->dcols; i++) { + s->dyncols[i].label = s->dyncols[i].column; + } + } + } else if (s->dcols == 1) { + s->dyncols[0].label = s->dyncols[0].column; + } + } + for (i = 0; i < s->dcols; i++) { + s->dyncols[i].type = + mapsqltype(s->dyncols[i].typename, &s->dyncols[i].nosign, *s->ov3, + s->nowchar[0] || s->nowchar[1], s->dobigint); + getmd(s->dyncols[i].typename, s->dyncols[i].type, + &s->dyncols[i].size, &s->dyncols[i].prec); +#ifdef SQL_LONGVARCHAR + if (s->dyncols[i].type == SQL_VARCHAR && + s->dyncols[i].size > 255) { + s->dyncols[i].type = SQL_LONGVARCHAR; + } +#endif +#ifdef WINTERFACE +#ifdef SQL_WLONGVARCHAR + if (s->dyncols[i].type == SQL_WVARCHAR && + s->dyncols[i].size > 255) { + s->dyncols[i].type = SQL_WLONGVARCHAR; + } +#endif +#endif + if (s->dyncols[i].type == SQL_VARBINARY && + s->dyncols[i].size > 255) { + s->dyncols[i].type = SQL_LONGVARBINARY; + } + } + for (i = 1, k = 0; i < s->dcols; i++) { + if (strcmp(s->dyncols[i].table, s->dyncols[0].table) == 0) { + k++; + } + } + s->one_tbl = (k && k + 1 == s->dcols) ? 1 : 0; + k = 0; + if (s->one_tbl) { + for (i = 0; i < s->dcols; i++) { + if (s->dyncols[i].ispk > 0) { + ++k; + if (s->has_rowid < 0 && s->dyncols[i].isrowid > 0) { + s->has_rowid = i; + } + } + } + } + s->has_pk = k; +} + +/** + * Return number of month days. + * @param year + * @param month 1..12 + * @result number of month days or 0 + */ + +static int +getmdays(int year, int month) +{ + static const int mdays[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int mday; + + if (month < 1) { + return 0; + } + mday = mdays[(month - 1) % 12]; + if (mday == 28 && year % 4 == 0 && + (!(year % 100 == 0) || year % 400 == 0)) { + mday++; + } + return mday; +} + +/** + * Convert string to ODBC DATE_STRUCT. + * @param str string to be converted + * @param ds output DATE_STRUCT + * @result 0 on success, -1 on error + * + * Strings of the format 'YYYYMMDD' or 'YYYY-MM-DD' or + * 'YYYY/MM/DD' or 'MM/DD/YYYY' are converted to a + * DATE_STRUCT. + */ + +static int +str2date(char *str, DATE_STRUCT *ds) +{ + int i, err = 0; + char *p, *q, sepc = '\0'; + + ds->year = ds->month = ds->day = 0; + p = str; + while (*p && !ISDIGIT(*p)) { + ++p; + } + q = p; + i = 0; + while (*q && !ISDIGIT(*q)) { + ++i; + ++q; + } + if (i >= 8) { + char buf[8]; + + strncpy(buf, p + 0, 4); buf[4] = '\0'; + ds->year = strtol(buf, NULL, 10); + strncpy(buf, p + 4, 2); buf[2] = '\0'; + ds->month = strtol(buf, NULL, 10); + strncpy(buf, p + 6, 2); buf[2] = '\0'; + ds->day = strtol(buf, NULL, 10); + goto done; + } + i = 0; + while (i < 3) { + int n; + + q = NULL; + n = strtol(p, &q, 10); + if (!q || q == p) { + if (*q == '\0') { + if (i == 0) { + err = 1; + } + goto done; + } + } + if (!sepc) { + sepc = *q; + } + if (*q == '-' || *q == '/' || *q == '\0' || i == 2) { + switch (i) { + case 0: ds->year = n; break; + case 1: ds->month = n; break; + case 2: ds->day = n; break; + } + ++i; + if (*q) { + ++q; + } + } else { + i = 0; + while (*q && !ISDIGIT(*q)) { + ++q; + } + } + p = q; + } +done: + /* final check for overflow */ + if (err || + ds->month < 1 || ds->month > 12 || + ds->day < 1 || ds->day > getmdays(ds->year, ds->month)) { + if (sepc == '/') { + /* Try MM/DD/YYYY format */ + int t[3]; + + t[0] = ds->year; + t[1] = ds->month; + t[2] = ds->day; + ds->year = t[2]; + ds->day = t[1]; + ds->month = t[0]; + if (ds->month >= 1 && ds->month <= 12 && + (ds->day >= 1 || ds->day <= getmdays(ds->year, ds->month))) { + return 0; + } + } + return -1; + } + return 0; +} + +/** + * Convert string to ODBC TIME_STRUCT. + * @param str string to be converted + * @param ts output TIME_STRUCT + * @result 0 on success, -1 on error + * + * Strings of the format 'HHMMSS' or 'HH:MM:SS' + * are converted to a TIME_STRUCT. + */ + +static int +str2time(char *str, TIME_STRUCT *ts) +{ + int i, err = 0, ampm = -1; + char *p, *q; + + ts->hour = ts->minute = ts->second = 0; + p = str; + while (*p && !ISDIGIT(*p)) { + ++p; + } + q = p; + i = 0; + while (*q && ISDIGIT(*q)) { + ++i; + ++q; + } + if (i >= 6) { + char buf[4]; + + strncpy(buf, p + 0, 2); buf[2] = '\0'; + ts->hour = strtol(buf, NULL, 10); + strncpy(buf, p + 2, 2); buf[2] = '\0'; + ts->minute = strtol(buf, NULL, 10); + strncpy(buf, p + 4, 2); buf[2] = '\0'; + ts->second = strtol(buf, NULL, 10); + goto done; + } + i = 0; + while (i < 3) { + int n; + + q = NULL; + n = strtol(p, &q, 10); + if (!q || q == p) { + if (*q == '\0') { + if (i == 0) { + err = 1; + } + goto done; + } + } + if (*q == ':' || *q == '\0' || i == 2) { + switch (i) { + case 0: ts->hour = n; break; + case 1: ts->minute = n; break; + case 2: ts->second = n; break; + } + ++i; + if (*q) { + ++q; + } + } else { + i = 0; + while (*q && !ISDIGIT(*q)) { + ++q; + } + } + p = q; + } + if (!err) { + while (*p) { + if ((p[0] == 'p' || p[0] == 'P') && + (p[1] == 'm' || p[1] == 'M')) { + ampm = 1; + } else if ((p[0] == 'a' || p[0] == 'A') && + (p[1] == 'm' || p[1] == 'M')) { + ampm = 0; + } + ++p; + } + if (ampm > 0) { + if (ts->hour < 12) { + ts->hour += 12; + } + } else if (ampm == 0) { + if (ts->hour == 12) { + ts->hour = 0; + } + } + } +done: + /* final check for overflow */ + if (err || ts->hour > 23 || ts->minute > 59 || ts->second > 59) { + return -1; + } + return 0; +} + +/** + * Convert string to ODBC TIMESTAMP_STRUCT. + * @param str string to be converted + * @param tss output TIMESTAMP_STRUCT + * @result 0 on success, -1 on error + * + * Strings of the format 'YYYYMMDDhhmmssff' or 'YYYY-MM-DD hh:mm:ss ff' + * or 'YYYY/MM/DD hh:mm:ss ff' or 'hh:mm:ss ff YYYY-MM-DD' are + * converted to a TIMESTAMP_STRUCT. The ISO8601 formats + * YYYY-MM-DDThh:mm:ss[.f]Z + * YYYY-MM-DDThh:mm:ss[.f]shh:mm + * are also supported. In case a time zone field is present, + * the resulting TIMESTAMP_STRUCT is expressed in UTC. + */ + +static int +str2timestamp(char *str, TIMESTAMP_STRUCT *tss) +{ + int i, m, n, err = 0, ampm = -1; + char *p, *q, in = '\0', sepc = '\0'; + + tss->year = tss->month = tss->day = 0; + tss->hour = tss->minute = tss->second = 0; + tss->fraction = 0; + p = str; + while (*p && !ISDIGIT(*p)) { + ++p; + } + q = p; + i = 0; + while (*q && ISDIGIT(*q)) { + ++i; + ++q; + } + if (i >= 14) { + char buf[16]; + + strncpy(buf, p + 0, 4); buf[4] = '\0'; + tss->year = strtol(buf, NULL, 10); + strncpy(buf, p + 4, 2); buf[2] = '\0'; + tss->month = strtol(buf, NULL, 10); + strncpy(buf, p + 6, 2); buf[2] = '\0'; + tss->day = strtol(buf, NULL, 10); + strncpy(buf, p + 8, 2); buf[2] = '\0'; + tss->hour = strtol(buf, NULL, 10); + strncpy(buf, p + 10, 2); buf[2] = '\0'; + tss->minute = strtol(buf, NULL, 10); + strncpy(buf, p + 12, 2); buf[2] = '\0'; + tss->second = strtol(buf, NULL, 10); + if (i > 14) { + m = i - 14; + strncpy(buf, p + 14, m); + while (m < 9) { + buf[m] = '0'; + ++m; + } + buf[m] = '\0'; + tss->fraction = strtol(buf, NULL, 10); + } + m = 7; + goto done; + } + m = i = 0; + while ((m & 7) != 7) { + q = NULL; + n = strtol(p, &q, 10); + if (!q || q == p) { + if (*q == '\0') { + if (m < 1) { + err = 1; + } + goto done; + } + } + if (in == '\0') { + switch (*q) { + case '-': + case '/': + if ((m & 1) == 0) { + in = *q; + i = 0; + } + break; + case ':': + if ((m & 2) == 0) { + in = *q; + i = 0; + } + break; + case ' ': + case '.': + break; + default: + in = '\0'; + i = 0; + break; + } + } + switch (in) { + case '-': + case '/': + if (!sepc) { + sepc = in; + } + switch (i) { + case 0: tss->year = n; break; + case 1: tss->month = n; break; + case 2: tss->day = n; break; + } + if (++i >= 3) { + i = 0; + m |= 1; + if (!(m & 2)) { + m |= 8; + } + goto skip; + } else { + ++q; + } + break; + case ':': + switch (i) { + case 0: tss->hour = n; break; + case 1: tss->minute = n; break; + case 2: tss->second = n; break; + } + if (++i >= 3) { + i = 0; + m |= 2; + if (*q == '.') { + in = '.'; + goto skip2; + } + if (*q == ' ') { + if ((m & 1) == 0) { + char *e = NULL; + + (void) strtol(q + 1, &e, 10); + if (e && *e == '-') { + goto skip; + } + } + in = '.'; + goto skip2; + } + goto skip; + } else { + ++q; + } + break; + case '.': + if (++i >= 1) { + int ndig = q - p; + + if (p[0] == '+' || p[0] == '-') { + ndig--; + } + while (ndig < 9) { + n = n * 10; + ++ndig; + } + tss->fraction = n; + m |= 4; + i = 0; + } + default: + skip: + in = '\0'; + skip2: + while (*q && !ISDIGIT(*q)) { + if ((q[0] == 'a' || q[0] == 'A') && + (q[1] == 'm' || q[1] == 'M')) { + ampm = 0; + ++q; + } else if ((q[0] == 'p' || q[0] == 'P') && + (q[1] == 'm' || q[1] == 'M')) { + ampm = 1; + ++q; + } + ++q; + } + } + p = q; + } + if ((m & 7) > 1 && (m & 8)) { + /* ISO8601 timezone */ + if (p > str && ISDIGIT(*p)) { + int nn, sign; + + q = p - 1; + if (*q != '+' && *q != '-') { + goto done; + } + sign = (*q == '+') ? -1 : 1; + q = NULL; + n = strtol(p, &q, 10); + if (!q || *q++ != ':' || !ISDIGIT(*q)) { + goto done; + } + p = q; + q = NULL; + nn = strtol(p, &q, 10); + tss->minute += nn * sign; + if ((SQLSMALLINT) tss->minute < 0) { + tss->hour -= 1; + tss->minute += 60; + } else if (tss->minute >= 60) { + tss->hour += 1; + tss->minute -= 60; + } + tss->hour += n * sign; + if ((SQLSMALLINT) tss->hour < 0) { + tss->day -= 1; + tss->hour += 24; + } else if (tss->hour >= 24) { + tss->day += 1; + tss->hour -= 24; + } + if ((short) tss->day < 1 || tss->day >= 28) { + int mday, pday, pmon; + + mday = getmdays(tss->year, tss->month); + pmon = tss->month - 1; + if (pmon < 1) { + pmon = 12; + } + pday = getmdays(tss->year, pmon); + if ((SQLSMALLINT) tss->day < 1) { + tss->month -= 1; + tss->day = pday; + } else if (tss->day > mday) { + tss->month += 1; + tss->day = 1; + } + if ((SQLSMALLINT) tss->month < 1) { + tss->year -= 1; + tss->month = 12; + } else if (tss->month > 12) { + tss->year += 1; + tss->month = 1; + } + } + } + } +done: + if ((m & 1) && + (tss->month < 1 || tss->month > 12 || + tss->day < 1 || tss->day > getmdays(tss->year, tss->month))) { + if (sepc == '/') { + /* Try MM/DD/YYYY format */ + int t[3]; + + t[0] = tss->year; + t[1] = tss->month; + t[2] = tss->day; + tss->year = t[2]; + tss->day = t[1]; + tss->month = t[0]; + } + } + /* Replace missing year/month/day with current date */ + if (!err && (m & 1) == 0) { +#ifdef _WIN32 + SYSTEMTIME t; + + GetLocalTime(&t); + tss->year = t.wYear; + tss->month = t.wMonth; + tss->day = t.wDay; +#else + struct timeval tv; + struct tm tm; + + gettimeofday(&tv, NULL); + tm = *localtime(&tv.tv_sec); + tss->year = tm.tm_year + 1900; + tss->month = tm.tm_mon + 1; + tss->day = tm.tm_mday; +#endif + } + /* Normalize fraction */ + if (tss->fraction < 0) { + tss->fraction = 0; + } + /* Final check for overflow */ + if (err || + tss->month < 1 || tss->month > 12 || + tss->day < 1 || tss->day > getmdays(tss->year, tss->month) || + tss->hour > 23 || tss->minute > 59 || tss->second > 59) { + return -1; + } + if ((m & 7) > 1) { + if (ampm > 0) { + if (tss->hour < 12) { + tss->hour += 12; + } + } else if (ampm == 0) { + if (tss->hour == 12) { + tss->hour = 0; + } + } + } + return ((m & 7) < 1) ? -1 : 0; +} + +/** + * Get boolean flag from string. + * @param string string to be inspected + * @result true or false + */ + +static int +getbool(char *string) +{ + if (string) { + return string[0] && strchr("Yy123456789Tt", string[0]) != NULL; + } + return 0; +} + +/** + * Internal function to release memory of blob + * @param dummy unused + * @param p pointer to memory + */ + +static void +blob_free(void *dummy, void *p) +{ + sqlite4_free(0, p); +} + +/** + * SQLite function to import a BLOB from a file + * @param ctx function context + * @param nargs number arguments + * @param args arguments + */ + +static void +blob_import(sqlite4_context *ctx, int nargs, sqlite4_value **args) +{ +#if 0 + DBC *d = (DBC *) sqlite4_user_data(ctx); +#endif + char *filename = 0; + + if (nargs > 0) { + if (sqlite4_value_type(args[0]) != SQLITE4_NULL) { + int nbytes; + + filename = (char *) sqlite4_value_text(args[0], &nbytes); + } + } + if (filename) { +#ifdef _WIN32 + char *wname = utf_to_wmb(filename, -1); + FILE *f; +#else + FILE *f = fopen(filename, "r"); +#endif + char *p; + long n, nn; + +#ifdef _WIN32 + if (wname) { + f = fopen(wname, "rb"); + } else { + sqlite4_result_error(ctx, "out of memory", -1); + return; + } + uc_free(wname); +#endif + if (f) { + if (fseek(f, 0, SEEK_END) == 0) { + n = ftell(f); + if (fseek(f, 0, SEEK_SET) == 0) { + p = sqlite4_malloc(0, n); + if (p) { + nn = fread(p, 1, n, f); + if (nn != n) { + sqlite4_result_error(ctx, "read error", -1); + sqlite4_free(0, p); + } else { + sqlite4_result_blob(ctx, p, n, blob_free, 0); + } + } else { + sqlite4_result_error(ctx, "out of memory", -1); + } + } else { + sqlite4_result_error(ctx, "seek error", -1); + } + } else { + sqlite4_result_error(ctx, "seek error", -1); + } + fclose(f); + } else { + sqlite4_result_error(ctx, "cannot open file", -1); + } + } else { + sqlite4_result_error(ctx, "no filename given", -1); + } +} + +/** + * SQLite function to export a BLOB to a file + * @param ctx function context + * @param nargs number arguments + * @param args arguments + */ + +static void +blob_export(sqlite4_context *ctx, int nargs, sqlite4_value **args) +{ +#if 0 + DBC *d = (DBC *) sqlite4_user_data(ctx); +#endif + char *filename = 0; + char *p = 0; + int n = 0; + + if (nargs > 0) { + p = (char *) sqlite4_value_blob(args[0], &n); + } + if (nargs > 1) { + if (sqlite4_value_type(args[1]) != SQLITE4_NULL) { + int nbytes; + + filename = (char *) sqlite4_value_text(args[1], &nbytes); + } + } + if (p) { + if (filename) { +#ifdef _WIN32 + char *wname = utf_to_wmb(filename, -1); + FILE *f; +#else + FILE *f = fopen(filename, "w"); +#endif + int nn; + +#ifdef _WIN32 + if (wname) { + f = fopen(wname, "wb"); + } else { + sqlite4_result_error(ctx, "out of memory", -1); + return; + } + uc_free(wname); +#endif + if (f) { + nn = fwrite(p, 1, n, f); + fclose(f); + if (nn != n) { + sqlite4_result_error(ctx, "write error", -1); + } else { + sqlite4_result_int(ctx, nn); + } + } else { + sqlite4_result_error(ctx, "cannot open file", -1); + } + } else { + sqlite4_result_error(ctx, "no filename given", -1); + } + } else { + sqlite4_result_null(ctx); + } +} + +/** + * SQLite trace or profile callback + * @param arg DBC pointer + * @param msg log message, SQL text + * @param et elapsed time + */ + +static void +dbtrace(void *arg, const char *msg, sqlite4_uint64 et) +{ + DBC *d = (DBC *) arg; + + if (msg && d->trace) { + int len = strlen(msg); + unsigned long s, f; + + if (len > 0) { + char *end = "\n"; + + if (msg[len - 1] != ';') { + end = ";\n"; + } + fprintf(d->trace, "%s%s", msg, end); + s = et / 1000000000LL; + f = et % 1000000000LL; + fprintf(d->trace, "-- took %lu.%09lu seconds\n", s, f); + fflush(d->trace); + } + } +} + +/** + * Trace function for SQLite API calls + * @param d pointer to database connection handle + * @param fn SQLite function name + * @param sql SQL string + */ + +static void +dbtraceapi(DBC *d, char *fn, const char *sql) +{ + if (fn && d->trace) { + if (sql) { + fprintf(d->trace, "-- %s: %s\n", fn, sql); + } else { + fprintf(d->trace, "-- %s\n", fn); + } + fflush(d->trace); + } +} + +/** + * Trace function for SQLite return codes + * @param d pointer to database connection handle + * @param rc SQLite return code + * @param err error string or NULL + */ + +static void +dbtracerc(DBC *d, int rc, char *err) +{ + if (rc != SQLITE4_OK && d->trace) { + fprintf(d->trace, "-- SQLITE ERROR CODE %d", rc); + fprintf(d->trace, err ? ": %s\n" : "\n", err); + fflush(d->trace); + } +} + +/** + * Open SQLite database file given file name and flags. + * @param d DBC pointer + * @param name file name + * @param isu true/false: file name is UTF8 encoded + * @param dsn data source name + * @param sflag STEPAPI flag + * @param spflag SyncPragma string + * @param ntflag NoTransaction string + * @param jmode JournalMode string + * @param busy busy/lock timeout + * @result ODBC error code + */ + +static SQLRETURN +dbopen(DBC *d, char *name, int isu, char *dsn, char *sflag, + char *spflag, char *ntflag, char *jmode, char *busy) +{ + char *endp = NULL; + int rc, tmp, busyto = 100000; + int flags = SQLITE4_OPEN_READWRITE | SQLITE4_OPEN_CREATE; + char *uname = name; + + if (d->sqlite) { + if (d->trace) { + fprintf(d->trace, "-- sqlite4_close (deferred): '%s'\n", + d->dbname); + fflush(d->trace); + } + sqlite4_close(d->sqlite, 0); + d->sqlite = NULL; + } + if (d->nocreat) { + flags &= ~ SQLITE4_OPEN_CREATE; + } +#if defined(_WIN32) || defined(_WIN64) + if (!isu) { + char expname[MAX_PATH]; + + expname[0] = '\0'; + rc = ExpandEnvironmentStrings(name, expname, sizeof (expname)); + if (rc <= sizeof (expname)) { + uname = wmb_to_utf(expname, rc - 1); + } else { + uname = wmb_to_utf(name, -1); + } + if (!uname) { + rc = SQLITE4_NOMEM; + setstatd(d, rc, "out of memory", (*d->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + } +#endif +#ifdef SQLITE4_OPEN_URI + flags |= SQLITE4_OPEN_URI; +#endif + rc = sqlite4_open(0, uname, &d->sqlite, /* flags ?? */ 0); +#if defined(WINTERFACE) || defined(_WIN32) || defined(_WIN64) + if (uname != name) { + uc_free(uname); + } +#endif + if (rc != SQLITE4_OK) { +connfail: + setstatd(d, rc, "connect failed", (*d->ov3) ? "HY000" : "S1000"); + if (d->sqlite) { + sqlite4_close(d->sqlite, 0); + d->sqlite = NULL; + } + return SQL_ERROR; + } + d->pwd = NULL; + d->pwdLen = 0; + if (d->trace) { + sqlite4_profile(d->sqlite, d, dbtrace, 0); + } + d->step_enable = getbool(sflag); + d->trans_disable = getbool(ntflag); + d->curtype = d->step_enable ? + SQL_CURSOR_FORWARD_ONLY : SQL_CURSOR_STATIC; + tmp = strtol(busy, &endp, 0); + if (endp && *endp == '\0' && endp != busy) { + busyto = tmp; + } + if (busyto < 1 || busyto > 1000000) { + busyto = 1000000; + } + d->timeout = busyto; + freep(&d->dbname); + d->dbname = xstrdup(name); + freep(&d->dsn); + d->dsn = xstrdup(dsn); + if ((rc = setsqliteopts(d->sqlite, d)) != SQLITE4_OK) { + if (d->trace) { + fprintf(d->trace, "-- sqlite4_close: '%s'\n", + d->dbname); + fflush(d->trace); + } + sqlite4_close(d->sqlite, 0); + d->sqlite = NULL; + goto connfail; + } + if (!spflag || spflag[0] == '\0') { + spflag = "NORMAL"; + } + if (spflag[0] != '\0') { + char syncp[128]; + + sprintf(syncp, "PRAGMA synchronous = %8.8s;", spflag); + sqlite4_exec(d->sqlite, syncp, NULL, NULL); + } + if (jmode[0] != '\0') { + char jourp[128]; + + sprintf(jourp, "PRAGMA journal_mode = %16.16s;", jmode); + sqlite4_exec(d->sqlite, jourp, NULL, NULL); + } + if (d->trace) { + fprintf(d->trace, "-- sqlite4_open: '%s'\n", d->dbname); + fflush(d->trace); + } +#if defined(_WIN32) || defined(_WIN64) + { + char pname[MAX_PATH]; + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, GetCurrentProcessId()); + + pname[0] = '\0'; + if (h) { + HMODULE m = NULL, l = LoadLibrary("psapi.dll"); + DWORD need; + typedef BOOL (WINAPI *epmfunc)(HANDLE, HMODULE *, DWORD, LPDWORD); + typedef BOOL (WINAPI *gmbfunc)(HANDLE, HMODULE, LPSTR, DWORD); + epmfunc epm; + gmbfunc gmb; + + if (l) { + epm = (epmfunc) GetProcAddress(l, "EnumProcessModules"); + gmb = (gmbfunc) GetProcAddress(l, "GetModuleBaseNameA"); + if (epm && gmb && epm(h, &m, sizeof (m), &need)) { + gmb(h, m, pname, sizeof (pname)); + } + FreeLibrary(l); + } + CloseHandle(h); + } + d->xcelqrx = strncasecmp(pname, "EXCEL", 5) == 0 || + strncasecmp(pname, "MSQRY", 5) == 0; + if (d->trace && d->xcelqrx) { + fprintf(d->trace, "-- enabled EXCEL quirks\n"); + fflush(d->trace); + } + } +#endif + sqlite4_create_function(d->sqlite, "blob_import", 1, d, + blob_import, 0, 0, 0); + sqlite4_create_function(d->sqlite, "blob_export", 2, d, + blob_export, 0, 0, 0); + return SQL_SUCCESS; +} + +/** + * Load SQLite extension modules, if any + * @param d DBC pointer + * @param exts string, comma separated extension names + */ + +static void +dbloadext(DBC *d, char *exts) +{ +#if 0 + char *p; + char path[SQL_MAX_MESSAGE_LENGTH]; + int plen = 0; + + if (!d->sqlite) { + return; + } +#if defined(_WIN32) || defined(_WIN64) + GetModuleFileName(hModule, path, sizeof (path)); + p = strrchr(path, '\\'); + plen = p ? ((p + 1) - path) : 0; +#endif + do { + p = strchr(exts, ','); + if (p) { + strncpy(path + plen, exts, p - exts); + path[plen + (p - exts)] = '\0'; + } else { + strcpy(path + plen, exts); + } + if (exts[0]) { + char *errmsg = NULL; + int rc; +#if defined(_WIN32) || defined(_WIN64) + char *q; + + q = path + plen; + if (!(q[0] && + ((q[1] == ':' && (q[2] == '\\' || q[2] == '/')) || + q[0] == '\\' || q[0] == '/' || q[0] == '.'))) { + q = path; + } + rc = sqlite4_load_extension(d->sqlite, q, 0, &errmsg); +#else + rc = sqlite4_load_extension(d->sqlite, path, 0, &errmsg); +#endif + if (rc != SQLITE4_OK) { +#if defined(_WIN32) || defined(_WIN64) + char buf[512], msg[512]; + + LoadString(hModule, IDS_EXTERR, buf, sizeof (buf)); + wsprintf(msg, buf, q, errmsg ? + errmsg : "no error info available"); + LoadString(hModule, IDS_EXTTITLE, buf, sizeof (buf)); + MessageBox(NULL, msg, buf, + MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); +#else + fprintf(stderr, "extension '%s' did not load%s%s\n", + path, errmsg ? ": " : "", errmsg ? errmsg : ""); +#endif + } + } + if (p) { + exts = p + 1; + } + } while (p); +#endif +} + +/** + * Find out column type + * @param s4stmt SQLite statement pointer + * @param col column number + * @param d DBC pointer (for tracing only) + * @param guessed_types flag array + * @result type name as string + */ + +static char * +s4stmt_coltype(sqlite4_stmt *s4stmt, int col, DBC *d, int *guessed_types) +{ + char *typename = (char *) sqlite4_column_decltype(s4stmt, col); + char guess[64]; + + guess[0] = '\0'; + if (!typename) { + int coltype = sqlite4_column_type(s4stmt, col); + + if (guessed_types) { + guessed_types[0]++; + } + if (d->trace) { + sprintf(guess, " (guessed from %d)", coltype); + } + switch (coltype) { + case SQLITE4_INTEGER: typename = "integer"; break; + case SQLITE4_FLOAT: typename = "double"; break; + default: + case SQLITE4_TEXT: typename = "varchar"; break; + case SQLITE4_BLOB: typename = "blob"; break; +#if 0 + case SQLITE4_NULL: typename = "null"; break; +#endif + } + } + if (d->trace) { + fprintf(d->trace, "-- column %d type%s: '%s'\n", col + 1, + guess, typename); + fflush(d->trace); + } + return typename; +} + +/** + * Add meta data for column + * @param s4stmt SQLite statement pointer + * @param col column number + * @param d DBC pointer (for tracing only) + * @param ci pointer to COL + */ + +static void +s4stmt_addmeta(sqlite4_stmt *s4stmt, int col, DBC *d, COL *ci) +{ + int nn = 0, pk = 0, ai = 0; + const char *dn, *tn, *cn, *dummy[4]; + + dn = sqlite4_column_database_name(s4stmt, col); + tn = sqlite4_column_table_name(s4stmt, col); + cn = sqlite4_column_origin_name(s4stmt, col); + dummy[0] = dummy[1] = 0; +#if 0 + sqlite4_table_column_metadata(d->sqlite, dn, tn, cn, + dummy, dummy + 1, + &nn, &pk, &ai); +#endif + ci->autoinc = ai ? SQL_TRUE: SQL_FALSE; + ci->notnull = nn ? SQL_NO_NULLS : SQL_NULLABLE; + ci->ispk = pk ? 1 : 0; + if (d->trace) { + fprintf(d->trace, "-- column %d %s\n", + col + 1, nn ? "notnull" : "nullable"); + if (ai) { + fprintf(d->trace, "-- column %d autoincrement\n", col + 1); + } + fflush(d->trace); + } + ci->isrowid = 0; + if (ci->ispk) { + nn = pk = ai = 0; + dummy[2] = dummy[3] = 0; + +#if 0 + sqlite4_table_column_metadata(d->sqlite, dn, tn, "rowid", + dummy + 2, dummy + 3, + &nn, &pk, &ai); +#endif + if (pk && dummy[0] && dummy[0] == dummy[2]) { + ci->isrowid = 1; + } + } +} + +/** + * Do one sqlite statement step gathering one result row + * @param s statement pointer + * @result ODBC error code + */ + +static int +s4stmt_step(STMT *s) +{ + DBC *d = (DBC *) s->dbc; + char **rowd = NULL; + const char *errp = NULL; + int i, ncols, rc; + + if (s != d->cur_s4stmt || !s->s4stmt) { + setstat(s, -1, "stale statement", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + rc = sqlite4_step(s->s4stmt); + if (rc == SQLITE4_ROW || rc == SQLITE4_DONE) { + ++s->s4stmt_rownum; + ncols = sqlite4_column_count(s->s4stmt); + if (d->s4stmt_needmeta && s->s4stmt_rownum == 0 && ncols > 0) { + PTRDIFF_T size; + char *p; + COL *dyncols; + const char *colname, *typename; + char *tblname; + + for (i = size = 0; i < ncols; i++) { + colname = sqlite4_column_name(s->s4stmt, i); + size += 3 + 3 * strlen(colname); + } + tblname = (char *) size; + for (i = 0; i < ncols; i++) { + p = (char *) sqlite4_column_table_name(s->s4stmt, i); + size += 2 + (p ? strlen(p) : 0); + } + dyncols = xmalloc(ncols * sizeof (COL) + size); + if (!dyncols) { + freedyncols(s); + s->ncols = 0; + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s->s4stmt); + s->s4stmt = NULL; + d->cur_s4stmt = NULL; + return nomem(s); + } + p = (char *) (dyncols + ncols); + tblname = p + (PTRDIFF_T) tblname; + for (i = 0; i < ncols; i++) { + char *q; + + colname = sqlite4_column_name(s->s4stmt, i); + if (d->trace) { + fprintf(d->trace, "-- column %d name: '%s'\n", + i + 1, colname); + fflush(d->trace); + } + q = (char *) sqlite4_column_table_name(s->s4stmt, i); + strcpy(tblname, q ? q : ""); + if (d->trace) { + fprintf(d->trace, "-- table %d name: '%s'\n", + i + 1, tblname); + fflush(d->trace); + } + dyncols[i].table = tblname; + tblname += strlen(tblname) + 1; + typename = s4stmt_coltype(s->s4stmt, i, d, 0); + dyncols[i].db = ((DBC *) (s->dbc))->dbname; + strcpy(p, colname); + dyncols[i].label = p; + p += strlen(p) + 1; + q = strchr(colname, '.'); + if (q) { + char *q2 = strchr(q + 1, '.'); + + /* SQLite 3.3.4 produces view.table.column sometimes */ + if (q2) { + q = q2; + } + } + if (q) { + strncpy(p, colname, q - colname); + p[q - colname] = '\0'; + p += strlen(p) + 1; + strcpy(p, q + 1); + dyncols[i].column = p; + p += strlen(p) + 1; + } else { + strcpy(p, colname); + dyncols[i].column = p; + p += strlen(p) + 1; + } + if (s->longnames) { + dyncols[i].column = dyncols[i].label; + } +#ifdef SQL_LONGVARCHAR + dyncols[i].type = SQL_LONGVARCHAR; + dyncols[i].size = 65535; +#else + dyncols[i].type = SQL_VARCHAR; + dyncols[i].size = 255; +#endif + dyncols[i].index = i; + dyncols[i].scale = 0; + dyncols[i].prec = 0; + dyncols[i].nosign = 1; + s4stmt_addmeta(s->s4stmt, i, d, &dyncols[i]); + dyncols[i].typename = xstrdup(typename); + } + freedyncols(s); + s->ncols = s->dcols = ncols; + s->dyncols = s->cols = dyncols; + fixupdyncols(s, d); + mkbindcols(s, s->ncols); + d->s4stmt_needmeta = 0; + } + if (ncols <= 0) { + goto killstmt; + } + if (rc == SQLITE4_DONE) { + freeresult(s, 0); + s->nrows = 0; + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s->s4stmt); + s->s4stmt = NULL; + d->cur_s4stmt = NULL; + return SQL_SUCCESS; + } + rowd = xmalloc((1 + 2 * ncols) * sizeof (char *)); + if (rowd) { + const char *value; + + rowd[0] = (char *) ((PTRDIFF_T) (ncols * 2)); + ++rowd; + for (i = 0; i < ncols; i++) { + int coltype = sqlite4_column_type(s->s4stmt, i); + + rowd[i] = rowd[i + ncols] = NULL; + if (coltype == SQLITE4_BLOB) { + int k, nbytes; + char *qp; + unsigned const char *bp; + + bp = sqlite4_column_blob(s->s4stmt, i, &nbytes); + qp = xmalloc(nbytes * 2 + 4); + if (qp) { + rowd[i + ncols] = qp; + *qp++ = 'X'; + *qp++ = '\''; + for (k = 0; k < nbytes; k++) { + *qp++ = xdigits[(bp[k] >> 4)]; + *qp++ = xdigits[(bp[k] & 0xF)]; + } + *qp++ = '\''; + *qp = '\0'; + } +#ifdef _MSC_VER + } else if (coltype == SQLITE4_FLOAT) { + static struct lconv *lc = 0; + double d = sqlite4_column_double(s->s4stmt, i); + char *p, buffer[128]; + + /* + * This avoids floating point rounding + * and formatting problems of some SQLite + * versions in conjunction with MSVC 2010. + */ + snprintf(buffer, sizeof (buffer), "%.15g", d); + if (!lc) { + lc = localeconv(); + } + if (lc && lc->decimal_point && lc->decimal_point[0] && + lc->decimal_point[0] != '.') { + p = strchr(buffer, lc->decimal_point[0]); + if (p) { + *p = '.'; + } + } + rowd[i + ncols] = xstrdup(buffer); +#endif + } else if (coltype != SQLITE4_NULL) { + int nbytes; + + value = sqlite4_column_text(s->s4stmt, i, &nbytes); + rowd[i + ncols] = xstrdup((char *) value); + } + } + for (i = 0; i < ncols; i++) { + int coltype = sqlite4_column_type(s->s4stmt, i); + + value = NULL; + if (coltype == SQLITE4_BLOB) { + int nbytes; + + value = sqlite4_column_blob(s->s4stmt, i, &nbytes); + } else if (coltype != SQLITE4_NULL) { + int nbytes; + + value = sqlite4_column_text(s->s4stmt, i, &nbytes); + } + if (value && !rowd[i + ncols]) { + freerows(rowd); + rowd = 0; + break; + } + } + } + if (rowd) { + freeresult(s, 0); + s->nrows = 1; + s->rows = rowd; + s->rowfree = freerows; + if (rc == SQLITE4_DONE) { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s->s4stmt); + s->s4stmt = NULL; + d->cur_s4stmt = NULL; + } + return SQL_SUCCESS; + } + } +killstmt: + dbtraceapi(d, "sqlite4_reset", 0); + rc = sqlite4_reset(s->s4stmt); + s->s4stmt_noreset = 1; + errp = sqlite4_errmsg(d->sqlite); + if (d->cur_s4stmt == s) { + d->cur_s4stmt = NULL; + } + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", rc); + return SQL_ERROR; +} + +/** + * Stop running sqlite statement + * @param s statement pointer + */ + +static void +s4stmt_end(STMT *s) +{ + DBC *d; + + if (!s || !s->s4stmt) { + return; + } + d = (DBC *) s->dbc; + if (d) { + d->busyint = 0; + } + if (!s->s4stmt_noreset) { + dbtraceapi(d, "sqlite4_reset", 0); + sqlite4_reset(s->s4stmt); + s->s4stmt_noreset = 1; + s->s4stmt_rownum = -1; + } + if (d->cur_s4stmt == s) { + d->cur_s4stmt = NULL; + } +} + +/** + * Conditionally stop running sqlite statement + * @param s statement pointer + */ + +static void +s4stmt_end_if(STMT *s) +{ + DBC *d = (DBC *) s->dbc; + + if (d) { + d->busyint = 0; + } + if (d && d->cur_s4stmt == s) { + s4stmt_end(s); + } +} + +/** + * Drop running sqlite statement in STMT + * @param s statement pointer + */ + +static void +s4stmt_drop(STMT *s) +{ + if (s->s4stmt) { + DBC *d = (DBC *) s->dbc; + + if (d) { + dbtraceapi(d, "sqlite4_finalize", 0); + } + sqlite4_finalize(s->s4stmt); + s->s4stmt = NULL; + s->s4stmt_rownum = 0; + } +} + +/** + * Start sqlite statement for execution of SELECT statement. + * @param s statement pointer + * @result ODBC error code + */ + +static SQLRETURN +s4stmt_start(STMT *s) +{ + DBC *d = (DBC *) s->dbc; + int sqlleft; + sqlite4_stmt *s4stmt = NULL; + int rc, nretry = 0; + + d->s4stmt_needmeta = 0; + if (!s->s4stmt) { + dbtraceapi(d, "sqlite4_prepare", (char *) s->query); + do { + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, (char *) s->query, -1, + &s4stmt, &sqlleft); + if (rc != SQLITE4_OK) { + if (s4stmt) { + sqlite4_finalize(s4stmt); + s4stmt = NULL; + } + } + } while (rc == SQLITE4_SCHEMA && (++nretry) < 2); + dbtracerc(d, rc, NULL); + if (rc != SQLITE4_OK) { + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + return SQL_ERROR; + } + if (sqlite4_bind_parameter_count(s4stmt) != s->nparams) { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s4stmt); + setstat(s, SQLITE4_ERROR, "parameter marker count incorrect", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + s->s4stmt = s4stmt; + s->s4stmt_noreset = 1; + d->s4stmt_needmeta = 1; + } + d->cur_s4stmt = s; + s->s4stmt_rownum = -1; + s4bind(d, s->s4stmt, s->nparams, s->bindparms); + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLDataSources(SQLHENV env, SQLUSMALLINT dir, SQLCHAR *srvname, + SQLSMALLINT buflen1, SQLSMALLINT *lenp1, + SQLCHAR *desc, SQLSMALLINT buflen2, SQLSMALLINT *lenp2) +{ + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + return SQL_ERROR; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLDataSourcesW(SQLHENV env, SQLUSMALLINT dir, SQLWCHAR *srvname, + SQLSMALLINT buflen1, SQLSMALLINT *lenp1, + SQLWCHAR *desc, SQLSMALLINT buflen2, SQLSMALLINT *lenp2) +{ + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + return SQL_ERROR; +} +#endif + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLDrivers(SQLHENV env, SQLUSMALLINT dir, SQLCHAR *drvdesc, + SQLSMALLINT descmax, SQLSMALLINT *desclenp, + SQLCHAR *drvattr, SQLSMALLINT attrmax, SQLSMALLINT *attrlenp) +{ + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + return SQL_ERROR; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLDriversW(SQLHENV env, SQLUSMALLINT dir, SQLWCHAR *drvdesc, + SQLSMALLINT descmax, SQLSMALLINT *desclenp, + SQLWCHAR *drvattr, SQLSMALLINT attrmax, SQLSMALLINT *attrlenp) +{ + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + return SQL_ERROR; +} +#endif + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLBrowseConnect(SQLHDBC dbc, SQLCHAR *connin, SQLSMALLINT conninLen, + SQLCHAR *connout, SQLSMALLINT connoutMax, + SQLSMALLINT *connoutLen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvunimpldbc(dbc); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLBrowseConnectW(SQLHDBC dbc, SQLWCHAR *connin, SQLSMALLINT conninLen, + SQLWCHAR *connout, SQLSMALLINT connoutMax, + SQLSMALLINT *connoutLen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvunimpldbc(dbc); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +/** + * Internal put (partial) parameter data into executing statement. + * @param stmt statement handle + * @param data pointer to data + * @param len length of data + * @result ODBC error code + */ + +static SQLRETURN +drvputdata(SQLHSTMT stmt, SQLPOINTER data, SQLLEN len) +{ + STMT *s; + int i, dlen, done = 0; + BINDPARM *p; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!s->query || s->nparams <= 0) { +seqerr: + setstat(s, -1, "sequence error", "HY010"); + return SQL_ERROR; + } + for (i = (s->pdcount < 0) ? 0 : s->pdcount; i < s->nparams; i++) { + p = &s->bindparms[i]; + if (p->need > 0) { + int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); + + if (len == SQL_NULL_DATA) { + freep(&p->parbuf); + p->param = NULL; + p->len = SQL_NULL_DATA; + p->need = -1; + } else if (type != SQL_C_CHAR +#ifdef WCHARSUPPORT + && type != SQL_C_WCHAR +#endif + && type != SQL_C_BINARY) { + int size = 0; + + switch (type) { + case SQL_C_TINYINT: + case SQL_C_UTINYINT: + case SQL_C_STINYINT: +#ifdef SQL_BIT + case SQL_C_BIT: +#endif + size = sizeof (SQLCHAR); + break; + case SQL_C_SHORT: + case SQL_C_USHORT: + case SQL_C_SSHORT: + size = sizeof (SQLSMALLINT); + break; + case SQL_C_LONG: + case SQL_C_ULONG: + case SQL_C_SLONG: + size = sizeof (SQLINTEGER); + break; +#ifdef SQL_BIGINT + case SQL_C_UBIGINT: + case SQL_C_SBIGINT: + size = sizeof (SQLBIGINT); + break; +#endif + case SQL_C_FLOAT: + size = sizeof (float); + break; + case SQL_C_DOUBLE: + size = sizeof (double); + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + size = sizeof (DATE_STRUCT); + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + size = sizeof (TIME_STRUCT); + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + size = sizeof (TIMESTAMP_STRUCT); + break; + } + freep(&p->parbuf); + p->parbuf = xmalloc(size); + if (!p->parbuf) { + return nomem(s); + } + p->param = p->parbuf; + memcpy(p->param, data, size); + p->len = size; + p->need = -1; + } else if (len == SQL_NTS && ( + type == SQL_C_CHAR +#ifdef WCHARSUPPORT + || type == SQL_C_WCHAR +#endif + )) { + char *dp = data; + +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + dp = uc_to_utf(data, len); + if (!dp) { + return nomem(s); + } + } +#endif +#if defined(_WIN32) || defined(_WIN64) + if (*s->oemcp) { + dp = wmb_to_utf(data, strlen (data)); + if (!dp) { + return nomem(s); + } + } +#endif + dlen = strlen(dp); + freep(&p->parbuf); + p->parbuf = xmalloc(dlen + 1); + if (!p->parbuf) { + if (dp != data) { + uc_free(dp); + } + return nomem(s); + } + p->param = p->parbuf; + strcpy(p->param, dp); + if (dp != data) { + uc_free(dp); + } + p->len = dlen; + p->need = -1; + } else if (len < 0) { + setstat(s, -1, "invalid length", "HY090"); + return SQL_ERROR; + } else { + dlen = min(p->len - p->offs, len); + if (!p->param) { + setstat(s, -1, "no memory for parameter", "HY013"); + return SQL_ERROR; + } + memcpy((char *) p->param + p->offs, data, dlen); + p->offs += dlen; + if (p->offs >= p->len) { +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + char *dp = uc_to_utf(p->param, p->len); + char *np; + int nlen; + + if (!dp) { + return nomem(s); + } + nlen = strlen(dp); + np = xmalloc(nlen + 1); + if (!np) { + uc_free(dp); + return nomem(s); + } + strcpy(np, dp); + uc_free(dp); + if (p->param == p->parbuf) { + freep(&p->parbuf); + } + p->parbuf = p->param = np; + p->len = nlen; + } else { + *((char *) p->param + p->len) = '\0'; + } + p->need = (type == SQL_C_CHAR || type == SQL_C_WCHAR) + ? -1 : 0; +#else + *((char *) p->param + p->len) = '\0'; + p->need = (type == SQL_C_CHAR) ? -1 : 0; +#endif +#if defined(_WIN32) || defined(_WIN64) + if (type == SQL_C_CHAR && *s->oemcp && + !(p->stype == SQL_BINARY || + p->stype == SQL_VARBINARY || + p->stype == SQL_LONGVARBINARY)) { + char *dp = wmb_to_utf(p->param, p->len); + + if (!dp) { + return nomem(s); + } + if (p->param == p->parbuf) { + freep(&p->parbuf); + } + p->parbuf = p->param = dp; + p->len = strlen(dp); + } + if (p->type == SQL_C_WCHAR && + (p->stype == SQL_VARCHAR || + p->stype == SQL_LONGVARCHAR) && + p->len == p->coldef * sizeof (SQLWCHAR)) { + /* fix for MS-Access */ + p->len = p->coldef; + } +#endif + } + } + done = 1; + break; + } + } + if (!done) { + goto seqerr; + } + return SQL_SUCCESS; +} + +/** + * Put (partial) parameter data into executing statement. + * @param stmt statement handle + * @param data pointer to data + * @param len length of data + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLPutData(SQLHSTMT stmt, SQLPOINTER data, SQLLEN len) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvputdata(stmt, data, len); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Clear out parameter bindings, if any. + * @param s statement pointer + */ + +static SQLRETURN +freeparams(STMT *s) +{ + if (s->bindparms) { + int n; + + for (n = 0; n < s->nbindparms; n++) { + freep(&s->bindparms[n].parbuf); + memset(&s->bindparms[n], 0, sizeof (BINDPARM)); + } + } + return SQL_SUCCESS; +} + +/** + * Setup sqlite4 parameter for statement parameter. + * @param s statement pointer + * @param sql sql string + * @param pnum parameter number + * @result ODBC error code + * + * The parameter is converted within BINDPARM in order to + * be presented to sqlite4_bind_*() functions. + */ + +static SQLRETURN +setupparam(STMT *s, char *sql, int pnum) +{ + int type, len = 0, needalloc = 0; + BINDPARM *p; + + if (!s->bindparms || pnum < 0 || pnum >= s->nbindparms) { + goto error; + } + p = &s->bindparms[pnum]; + type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); +#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) + /* MS Access hack part 4 (map SQL_C_DEFAULT to SQL_C_CHAR) */ + if (type == SQL_C_WCHAR && p->type == SQL_C_DEFAULT) { + type = SQL_C_CHAR; + } +#endif + if (p->need > 0) { + return setupparbuf(s, p); + } + p->strbuf[0] = '\0'; + if (!p->param || (p->lenp && *p->lenp == SQL_NULL_DATA)) { + p->s4type = SQLITE4_NULL; + p->s4size = 0; + return SQL_SUCCESS; + } + if (type == SQL_C_CHAR && + (p->stype == SQL_BINARY || + p->stype == SQL_VARBINARY || + p->stype == SQL_LONGVARBINARY)) { + type = SQL_C_BINARY; + } + switch (type) { + case SQL_C_BINARY: + p->s4type = SQLITE4_BLOB; + p->s4size = p->len; + p->s4val = p->param; + if (p->need < 0) { + break; + } + if (!p->lenp) { + len = p->len; + } else if (*p->lenp == SQL_DATA_AT_EXEC) { + len = p->len; + } else { + len = *p->lenp; + if (len <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + len = SQL_LEN_DATA_AT_EXEC(len); + } + } + if (len < 0) { + setstat(s, -1, "invalid length", "HY009"); + return SQL_ERROR; + } + p->len = len; + p->max = p->len; + p->need = -1; + p->s4size = len; + break; +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: +#endif + case SQL_C_CHAR: + p->s4type = SQLITE4_TEXT; + p->s4size = -1; + p->s4val = p->param; + if (!p->parbuf) { +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + if (!p->lenp || *p->lenp == SQL_NTS) { + p->max = uc_strlen(p->param) * sizeof (SQLWCHAR); + } else if (*p->lenp >= 0) { + p->max = *p->lenp; + } + } else +#endif + if (type == SQL_C_CHAR) { + if (!p->lenp || *p->lenp == SQL_NTS) { + p->len = p->max = strlen(p->param); +#if defined(_WIN32) || defined(_WIN64) + needalloc = 1; +#endif + } else if (*p->lenp >= 0) { + p->len = p->max = *p->lenp; + needalloc = 1; + } + } + } + if (p->need < 0 && p->parbuf == p->param) { + break; + } +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + char *dp = uc_to_utf(p->param, p->max); + + if (!dp) { + return nomem(s); + } + if (p->param == p->parbuf) { + freep(&p->parbuf); + } + p->parbuf = p->param = dp; + p->need = -1; + p->len = strlen(p->param); + p->s4val = p->param; + p->s4size = p->len; + } else +#endif + if (type == SQL_C_CHAR) { + p->s4val = p->param; + if (needalloc) { + char *dp; + +#if defined(_WIN32) || defined(_WIN64) + if (*s->oemcp) { + dp = wmb_to_utf(p->param, p->len); + } else { + dp = xmalloc(p->len + 1); + } +#else + dp = xmalloc(p->len + 1); +#endif + if (!dp) { + return nomem(s); + } +#if defined(_WIN32) || defined(_WIN64) + if (*s->oemcp) { + p->len = strlen(dp); + } else { + memcpy(dp, p->param, p->len); + dp[p->len] = '\0'; + } +#else + memcpy(dp, p->param, p->len); + dp[p->len] = '\0'; +#endif + if (p->param == p->parbuf) { + freep(&p->parbuf); + } + p->parbuf = p->param = dp; + p->need = -1; + p->s4val = p->param; + p->s4size = p->len; + } + } + break; + case SQL_C_UTINYINT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = *((SQLCHAR *) p->param); + break; + case SQL_C_USHORT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = *((SQLUSMALLINT *) p->param); + break; + case SQL_C_SHORT: + case SQL_C_SSHORT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = *((SQLSMALLINT *) p->param); + break; + case SQL_C_ULONG: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = *((SQLUINTEGER *) p->param); + break; + case SQL_C_LONG: + case SQL_C_SLONG: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = *((SQLINTEGER *) p->param); + break; +#ifdef SQL_BIT + case SQL_C_BIT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (int); + p->s4ival = (*((SQLCHAR *) p->param)) ? 1 : 0; + break; +#endif +#ifdef SQL_BIGINT + case SQL_C_SBIGINT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (sqlite4_int64); + p->s4lival = *((sqlite4_int64 *) p->param); + break; + case SQL_C_UBIGINT: + p->s4type = SQLITE4_INTEGER; + p->s4size = sizeof (sqlite4_int64); + p->s4lival = *((sqlite4_uint64 *) p->param); + break; +#endif + case SQL_C_FLOAT: + p->s4type = SQLITE4_FLOAT; + p->s4size = sizeof (double); + p->s4dval = *((float *) p->param); + break; + case SQL_C_DOUBLE: + p->s4type = SQLITE4_FLOAT; + p->s4size = sizeof (double); + p->s4dval = *((double *) p->param); + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + sprintf(p->strbuf, "%04d-%02d-%02d", + ((DATE_STRUCT *) p->param)->year, + ((DATE_STRUCT *) p->param)->month, + ((DATE_STRUCT *) p->param)->day); + p->s4type = SQLITE4_TEXT; + p->s4size = -1; + p->s4val = p->strbuf; + break; +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + sprintf(p->strbuf, "%02d:%02d:%02d", + ((TIME_STRUCT *) p->param)->hour, + ((TIME_STRUCT *) p->param)->minute, + ((TIME_STRUCT *) p->param)->second); + p->s4type = SQLITE4_TEXT; + p->s4size = -1; + p->s4val = p->strbuf; + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + len = (int) ((TIMESTAMP_STRUCT *) p->param)->fraction; + len /= 1000000; + len = len % 1000; + if (len < 0) { + len = 0; + } + if (p->coldef && p->coldef <= 16) { + sprintf(p->strbuf, "%04d-%02d-%02d %02d:%02d:00.000", + ((TIMESTAMP_STRUCT *) p->param)->year, + ((TIMESTAMP_STRUCT *) p->param)->month, + ((TIMESTAMP_STRUCT *) p->param)->day, + ((TIMESTAMP_STRUCT *) p->param)->hour, + ((TIMESTAMP_STRUCT *) p->param)->minute); + } else if (p->coldef && p->coldef <= 19) { + sprintf(p->strbuf, "%04d-%02d-%02d %02d:%02d:%02d.000", + ((TIMESTAMP_STRUCT *) p->param)->year, + ((TIMESTAMP_STRUCT *) p->param)->month, + ((TIMESTAMP_STRUCT *) p->param)->day, + ((TIMESTAMP_STRUCT *) p->param)->hour, + ((TIMESTAMP_STRUCT *) p->param)->minute, + ((TIMESTAMP_STRUCT *) p->param)->second); + } else { + sprintf(p->strbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", + ((TIMESTAMP_STRUCT *) p->param)->year, + ((TIMESTAMP_STRUCT *) p->param)->month, + ((TIMESTAMP_STRUCT *) p->param)->day, + ((TIMESTAMP_STRUCT *) p->param)->hour, + ((TIMESTAMP_STRUCT *) p->param)->minute, + ((TIMESTAMP_STRUCT *) p->param)->second, + len); + } + p->s4type = SQLITE4_TEXT; + p->s4size = -1; + p->s4val = p->strbuf; + break; + default: + error: + setstat(s, -1, "unsupported parameter type", + (*s->ov3) ? "07009" : "S1093"); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +/** + * Internal bind parameter on HSTMT. + * @param stmt statement handle + * @param pnum parameter number, starting at 1 + * @param iotype input/output type of parameter + * @param buftype type of host variable + * @param ptype + * @param coldef + * @param scale + * @param data pointer to host variable + * @param buflen length of host variable + * @param len output length pointer + * @result ODBC error code + */ + +static SQLRETURN +drvbindparam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT iotype, + SQLSMALLINT buftype, SQLSMALLINT ptype, SQLUINTEGER coldef, + SQLSMALLINT scale, + SQLPOINTER data, SQLINTEGER buflen, SQLLEN *len) +{ + STMT *s; + BINDPARM *p; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (pnum == 0) { + setstat(s, -1, "invalid parameter", (*s->ov3) ? "07009" : "S1093"); + return SQL_ERROR; + } + if (!data && !len) { + setstat(s, -1, "invalid buffer", "HY003"); + return SQL_ERROR; + } + --pnum; + if (s->bindparms) { + if (pnum >= s->nbindparms) { + BINDPARM *newparms; + + newparms = xrealloc(s->bindparms, + (pnum + 1) * sizeof (BINDPARM)); + if (!newparms) { +outofmem: + return nomem(s); + } + s->bindparms = newparms; + memset(&s->bindparms[s->nbindparms], 0, + (pnum + 1 - s->nbindparms) * sizeof (BINDPARM)); + s->nbindparms = pnum + 1; + } + } else { + int npar = max(10, pnum + 1); + + s->bindparms = xmalloc(npar * sizeof (BINDPARM)); + if (!s->bindparms) { + goto outofmem; + } + memset(s->bindparms, 0, npar * sizeof (BINDPARM)); + s->nbindparms = npar; + } + switch (buftype) { + case SQL_C_STINYINT: + case SQL_C_UTINYINT: + case SQL_C_TINYINT: +#ifdef SQL_C_BIT + case SQL_C_BIT: +#endif + buflen = sizeof (SQLCHAR); + break; + case SQL_C_SHORT: + case SQL_C_USHORT: + case SQL_C_SSHORT: + buflen = sizeof (SQLSMALLINT); + break; + case SQL_C_SLONG: + case SQL_C_ULONG: + case SQL_C_LONG: + buflen = sizeof (SQLINTEGER); + break; + case SQL_C_FLOAT: + buflen = sizeof (float); + break; + case SQL_C_DOUBLE: + buflen = sizeof (double); + break; + case SQL_C_TIMESTAMP: +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + buflen = sizeof (TIMESTAMP_STRUCT); + break; + case SQL_C_TIME: +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + buflen = sizeof (TIME_STRUCT); + break; + case SQL_C_DATE: +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + buflen = sizeof (DATE_STRUCT); + break; +#ifdef SQL_C_UBIGINT + case SQL_C_UBIGINT: + buflen = sizeof (SQLBIGINT); + break; +#endif +#ifdef SQL_C_SBIGINT + case SQL_C_SBIGINT: + buflen = sizeof (SQLBIGINT); + break; +#endif +#ifdef SQL_C_BIGINT + case SQL_C_BIGINT: + buflen = sizeof (SQLBIGINT); + break; +#endif + } + p = &s->bindparms[pnum]; + p->type = buftype; + p->stype = ptype; + p->coldef = coldef; + p->scale = scale; + p->max = buflen; + p->inc = buflen; + p->lenp = p->lenp0 = len; + p->offs = 0; + p->len = 0; + p->param0 = data; + freep(&p->parbuf); + p->param = p->param0; + p->bound = 1; + p->need = 0; + return SQL_SUCCESS; +} + +/** + * Bind parameter on HSTMT. + * @param stmt statement handle + * @param pnum parameter number, starting at 1 + * @param iotype input/output type of parameter + * @param buftype type of host variable + * @param ptype + * @param coldef + * @param scale + * @param data pointer to host variable + * @param buflen length of host variable + * @param len output length pointer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLBindParameter(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT iotype, + SQLSMALLINT buftype, SQLSMALLINT ptype, SQLULEN coldef, + SQLSMALLINT scale, + SQLPOINTER data, SQLLEN buflen, SQLLEN *len) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbindparam(stmt, pnum, iotype, buftype, ptype, coldef, + scale, data, buflen, len); + HSTMT_UNLOCK(stmt); + return ret; +} + +#ifndef HAVE_IODBC +/** + * Bind parameter on HSTMT. + * @param stmt statement handle + * @param pnum parameter number, starting at 1 + * @param vtype input/output type of parameter + * @param ptype + * @param lenprec + * @param scale + * @param val pointer to host variable + * @param lenp output length pointer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLBindParam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT vtype, + SQLSMALLINT ptype, SQLULEN lenprec, + SQLSMALLINT scale, SQLPOINTER val, + SQLLEN *lenp) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbindparam(stmt, pnum, SQL_PARAM_INPUT, vtype, ptype, + lenprec, scale, val, 0, lenp); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Return number of parameters. + * @param stmt statement handle + * @param nparam output parameter count + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLNumParams(SQLHSTMT stmt, SQLSMALLINT *nparam) +{ + STMT *s; + SQLSMALLINT dummy; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!nparam) { + nparam = &dummy; + } + *nparam = s->nparams; + HSTMT_UNLOCK(stmt); + return SQL_SUCCESS; +} + +/** + * Setup parameter buffer for deferred parameter. + * @param s pointer to STMT + * @param p pointer to BINDPARM + * @result ODBC error code (success indicated by SQL_NEED_DATA) + */ + +static SQLRETURN +setupparbuf(STMT *s, BINDPARM *p) +{ + if (!p->parbuf) { + if (*p->lenp == SQL_DATA_AT_EXEC) { + p->len = p->max; + } else { + p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp); + } + if (p->len < 0 && p->len != SQL_NTS && + p->len != SQL_NULL_DATA) { + setstat(s, -1, "invalid length", "HY009"); + return SQL_ERROR; + } + if (p->len >= 0) { + p->parbuf = xmalloc(p->len + 2); + if (!p->parbuf) { + return nomem(s); + } + p->param = p->parbuf; + } else { + p->param = NULL; + } + } + return SQL_NEED_DATA; +} + +/** + * Retrieve next parameter for sending data to executing query. + * @param stmt statement handle + * @param pind pointer to output parameter indicator + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind) +{ + STMT *s; + int i; + SQLPOINTER dummy; + SQLRETURN ret; + BINDPARM *p; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!pind) { + pind = &dummy; + } + if (s->pdcount < s->nparams) { + s->pdcount++; + } + for (i = 0; i < s->pdcount; i++) { + p = &s->bindparms[i]; + if (p->need > 0) { + int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); + + p->need = (type == SQL_C_CHAR || type == SQL_C_WCHAR) ? -1 : 0; + } + } + for (; i < s->nparams; i++) { + p = &s->bindparms[i]; + if (p->need > 0) { + *pind = (SQLPOINTER) p->param0; + ret = setupparbuf(s, p); + s->pdcount = i; + goto done; + } + } + ret = drvexecute(stmt, 0); +done: + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Return information about parameter. + * @param stmt statement handle + * @param pnum parameter number, starting at 1 + * @param dtype output type indicator + * @param size output size indicator + * @param decdigits output number of digits + * @param nullable output NULL allowed indicator + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDescribeParam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT *dtype, + SQLULEN *size, SQLSMALLINT *decdigits, SQLSMALLINT *nullable) +{ + STMT *s; + SQLRETURN ret = SQL_ERROR; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + --pnum; + if (pnum >= s->nparams) { + setstat(s, -1, "invalid parameter index", + (*s->ov3) ? "HY000" : "S1000"); + goto done; + } + if (dtype) { +#ifdef SQL_LONGVARCHAR +#ifdef WINTERFACE + *dtype = s->nowchar[0] ? SQL_LONGVARCHAR : SQL_WLONGVARCHAR; +#else + *dtype = SQL_LONGVARCHAR; +#endif +#else +#ifdef WINTERFACE + *dtype = s->nowchar[0] ? SQL_VARCHAR : SQL_WVARCHAR; +#else + *dtype = SQL_VARCHAR; +#endif +#endif + } + if (size) { +#ifdef SQL_LONGVARCHAR + *size = 65536; +#else + *size = 255; +#endif + } + if (decdigits) { + *decdigits = 0; + } + if (nullable) { + *nullable = SQL_NULLABLE; + } + ret = SQL_SUCCESS; +done: + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Set information on parameter. + * @param stmt statement handle + * @param par parameter number, starting at 1 + * @param type type of host variable + * @param sqltype + * @param coldef + * @param scale + * @param val pointer to host variable + * @param nval output length pointer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetParam(SQLHSTMT stmt, SQLUSMALLINT par, SQLSMALLINT type, + SQLSMALLINT sqltype, SQLULEN coldef, + SQLSMALLINT scale, SQLPOINTER val, SQLLEN *nval) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbindparam(stmt, par, SQL_PARAM_INPUT, + type, sqltype, coldef, scale, val, + SQL_SETPARAM_VALUE_MAX, nval); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLParamOptions(SQLHSTMT stmt, SQLULEN rows, SQLULEN *rowp) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvunimplstmt(stmt); + HSTMT_UNLOCK(stmt); + return ret; +} + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLGetDescField(SQLHDESC handle, SQLSMALLINT recno, + SQLSMALLINT fieldid, SQLPOINTER value, + SQLINTEGER buflen, SQLINTEGER *strlen) +{ + return SQL_ERROR; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLGetDescFieldW(SQLHDESC handle, SQLSMALLINT recno, + SQLSMALLINT fieldid, SQLPOINTER value, + SQLINTEGER buflen, SQLINTEGER *strlen) +{ + return SQL_ERROR; +} +#endif + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLSetDescField(SQLHDESC handle, SQLSMALLINT recno, + SQLSMALLINT fieldid, SQLPOINTER value, + SQLINTEGER buflen) +{ + return SQL_ERROR; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLSetDescFieldW(SQLHDESC handle, SQLSMALLINT recno, + SQLSMALLINT fieldid, SQLPOINTER value, + SQLINTEGER buflen) +{ + return SQL_ERROR; +} +#endif + +#ifndef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLGetDescRec(SQLHDESC handle, SQLSMALLINT recno, + SQLCHAR *name, SQLSMALLINT buflen, + SQLSMALLINT *strlen, SQLSMALLINT *type, + SQLSMALLINT *subtype, SQLLEN *len, + SQLSMALLINT *prec, SQLSMALLINT *scale, + SQLSMALLINT *nullable) +{ + return SQL_ERROR; +} +#endif + +#ifdef WINTERFACE +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLGetDescRecW(SQLHDESC handle, SQLSMALLINT recno, + SQLWCHAR *name, SQLSMALLINT buflen, + SQLSMALLINT *strlen, SQLSMALLINT *type, + SQLSMALLINT *subtype, SQLLEN *len, + SQLSMALLINT *prec, SQLSMALLINT *scale, + SQLSMALLINT *nullable) +{ + return SQL_ERROR; +} +#endif + +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLSetDescRec(SQLHDESC handle, SQLSMALLINT recno, + SQLSMALLINT type, SQLSMALLINT subtype, + SQLLEN len, SQLSMALLINT prec, + SQLSMALLINT scale, SQLPOINTER data, + SQLLEN *strlen, SQLLEN *indicator) +{ + return SQL_ERROR; +} + +/** + * Setup empty result set from constant column specification. + * @param stmt statement handle + * @param colspec column specification array (default, ODBC2) + * @param ncols number of columns (default, ODBC2) + * @param colspec3 column specification array (ODBC3) + * @param ncols4 number of columns (ODBC3) + * @param nret returns number of columns + * @result ODBC error code + */ + +static SQLRETURN +mkresultset(HSTMT stmt, COL *colspec, int ncols, COL *colspec3, + int ncols4, int *nret) +{ + STMT *s; + DBC *d; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (s->dbc == SQL_NULL_HDBC) { +noconn: + return noconn(s); + } + d = (DBC *) s->dbc; + if (!d->sqlite) { + goto noconn; + } + s4stmt_end_if(s); + freeresult(s, 0); + if (colspec3 && *s->ov3) { + s->ncols = ncols4; + s->cols = colspec3; + } else { + s->ncols = ncols; + s->cols = colspec; + } + mkbindcols(s, s->ncols); + s->nowchar[1] = 1; + s->nrows = 0; + s->rowp = s->rowprs = -1; + s->isselect = -1; + if (nret) { + *nret = s->ncols; + } + return SQL_SUCCESS; +} + +/** + * Columns for result set of SQLTablePrivileges(). + */ + +static COL tablePrivSpec2[] = { + { "SYSTEM", "TABLEPRIV", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "TABLEPRIV", "GRANTOR", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "GRANTEE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "IS_GRANTABLE", SCOL_VARCHAR, 50 } +}; + +static COL tablePrivSpec3[] = { + { "SYSTEM", "TABLEPRIV", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "TABLEPRIV", "GRANTOR", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "GRANTEE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TABLEPRIV", "IS_GRANTABLE", SCOL_VARCHAR, 50 } +}; + +/** + * Retrieve privileges on tables and/or views. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvtableprivileges(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen) +{ + SQLRETURN ret; + STMT *s; + DBC *d; + int ncols, rc, size, npatt; + char *errp = NULL, *sql, tname[512]; + + ret = mkresultset(stmt, tablePrivSpec2, array_size(tablePrivSpec2), + tablePrivSpec3, array_size(tablePrivSpec3), NULL); + if (ret != SQL_SUCCESS) { + return ret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') { + table = NULL; + goto doit; + } + if (schema && (schemaLen > 0 || schemaLen == SQL_NTS) && + schema[0] == '%') { + if ((!cat || catLen == 0 || !cat[0]) && + (!table || tableLen == 0 || !table[0])) { + table = NULL; + goto doit; + } + } +doit: + if (!table) { + size = 1; + tname[0] = '%'; + } else { + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + } + tname[size] = '\0'; + npatt = unescpat(tname); +#if defined(_WIN32) || defined(_WIN64) + sql = sqlite4_mprintf(0, "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'SELECT' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'UPDATE' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'DELETE' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'INSERT' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'REFERENCES' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q", + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + npatt ? "like" : "=", tname, + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + npatt ? "like" : "=", tname, + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + npatt ? "like" : "=", tname, + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + npatt ? "like" : "=", tname, + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + npatt ? "like" : "=", tname); +#else + sql = sqlite4_mprintf(0, "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'SELECT' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'UPDATE' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'DELETE' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'INSERT' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q " + "UNION " + "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "'' as 'GRANTOR', " + "'' as 'GRANTEE', " + "'REFERENCES' AS 'PRIVILEGE', " + "NULL as 'IS_GRANTABLE' " + "from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q", + npatt ? "like" : "=", tname, + npatt ? "like" : "=", tname, + npatt ? "like" : "=", tname, + npatt ? "like" : "=", tname, + npatt ? "like" : "=", tname); +#endif + if (!sql) { + return nomem(s); + } + ret = starttran(s); + if (ret != SQL_SUCCESS) { + sqlite4_free(0, sql); + return ret; + } + dbtraceapi(d, "sqlite4_get_table", sql); + rc = sqlite4_get_table(d->sqlite, sql, &s->rows, &s->nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (rc == SQLITE4_OK) { + if (ncols != s->ncols) { + freeresult(s, 0); + s->nrows = 0; + } else { + s->rowfree = freerows; + } + } else { + s->nrows = 0; + s->rows = NULL; + s->rowfree = NULL; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + s->rowp = s->rowprs = -1; + return SQL_SUCCESS; +} + +#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) +/** + * Retrieve privileges on tables and/or views. + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLTablePrivileges(SQLHSTMT stmt, + SQLCHAR *catalog, SQLSMALLINT catalogLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvtableprivileges(stmt, catalog, catalogLen, schema, schemaLen, + table, tableLen); + goto done2; + } + if (catalog) { + c = wmb_to_utf_c((char *) catalog, catalogLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvtableprivileges(stmt, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS); +#else + ret = drvtableprivileges(stmt, catalog, catalogLen, schema, schemaLen, + table, tableLen); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#if !defined(HAVE_UNIXODBC) || !(HAVE_UNIXODBC) +#ifdef WINTERFACE +/** + * Retrieve privileges on tables and/or views (UNICODE version). + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLTablePrivilegesW(SQLHSTMT stmt, + SQLWCHAR *catalog, SQLSMALLINT catalogLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen) +{ + char *c = NULL, *s = NULL, *t = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (catalog) { + c = uc_to_utf_c(catalog, catalogLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvtableprivileges(stmt, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS); +done: + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif +#endif + +/** + * Columns for result set of SQLColumnPrivileges(). + */ + +static COL colPrivSpec2[] = { + { "SYSTEM", "COLPRIV", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLPRIV", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLPRIV", "GRANTOR", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "GRANTEE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 } +}; + +static COL colPrivSpec3[] = { + { "SYSTEM", "COLPRIV", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLPRIV", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLPRIV", "GRANTOR", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "GRANTEE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 } +}; + +#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) +/** + * Retrieve privileges on columns. + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param column column name or NULL + * @param columnLen length of column name or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColumnPrivileges(SQLHSTMT stmt, + SQLCHAR *catalog, SQLSMALLINT catalogLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLCHAR *column, SQLSMALLINT columnLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, colPrivSpec2, array_size(colPrivSpec2), + colPrivSpec3, array_size(colPrivSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#if !defined(HAVE_UNIXODBC) || !(HAVE_UNIXODBC) +#ifdef WINTERFACE +/** + * Retrieve privileges on columns (UNICODE version). + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param column column name or NULL + * @param columnLen length of column name or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColumnPrivilegesW(SQLHSTMT stmt, + SQLWCHAR *catalog, SQLSMALLINT catalogLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen, + SQLWCHAR *column, SQLSMALLINT columnLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, colPrivSpec2, array_size(colPrivSpec2), + colPrivSpec3, array_size(colPrivSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif +#endif + +/** + * Columns for result set of SQLPrimaryKeys(). + */ + +static COL pkeySpec2[] = { + { "SYSTEM", "PRIMARYKEY", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PRIMARYKEY", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PRIMARYKEY", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PRIMARYKEY", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PRIMARYKEY", "KEY_SEQ", SQL_SMALLINT, 50 }, + { "SYSTEM", "PRIMARYKEY", "PK_NAME", SCOL_VARCHAR, 50 } +}; + +static COL pkeySpec3[] = { + { "SYSTEM", "PRIMARYKEY", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PRIMARYKEY", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PRIMARYKEY", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PRIMARYKEY", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PRIMARYKEY", "KEY_SEQ", SQL_SMALLINT, 50 }, + { "SYSTEM", "PRIMARYKEY", "PK_NAME", SCOL_VARCHAR, 50 } +}; + +/** + * Internal retrieve information about indexed columns. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvprimarykeys(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen) +{ + STMT *s; + DBC *d; + SQLRETURN sret; + int i, asize, ret, nrows, ncols, nrows2 = 0, ncols2 = 0; + int namec = -1, uniquec = -1, namec2 = -1, uniquec2 = -1, offs, seq = 1; + PTRDIFF_T size; + char **rowp = NULL, **rowp2 = NULL, *errp = NULL, *sql, tname[512]; + + sret = mkresultset(stmt, pkeySpec2, array_size(pkeySpec2), + pkeySpec3, array_size(pkeySpec3), &asize); + if (sret != SQL_SUCCESS) { + return sret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (!table || table[0] == '\0' || table[0] == '%') { + setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + tname[size] = '\0'; + unescpat(tname); + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", tname); + if (!sql) { + return nomem(s); + } + sret = starttran(s); + if (sret != SQL_SUCCESS) { + sqlite4_free(0, sql); + return sret; + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + size = 0; + if (ncols * nrows > 0) { + int typec; + + namec = findcol(rowp, ncols, "name"); + uniquec = findcol(rowp, ncols, "pk"); + typec = findcol(rowp, ncols, "type"); + if (namec >= 0 && uniquec >= 0 && typec >= 0) { + for (i = 1; i <= nrows; i++) { + if (*rowp[i * ncols + uniquec] != '0') { + size++; + } + } + } + } + if (size == 0) { + sql = sqlite4_mprintf(0, "PRAGMA index_list(%Q)", tname); + if (!sql) { + freerows(rowp); + return nomem(s); + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp2, &nrows2, &ncols2, + &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + freerows(rowp); + freerows(rowp2); + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + } + if (ncols2 * nrows2 > 0) { + namec2 = findcol(rowp2, ncols2, "name"); + uniquec2 = findcol(rowp2, ncols2, "unique"); + if (namec2 >= 0 && uniquec2 >= 0) { + for (i = 1; i <= nrows2; i++) { + int nnrows, nncols, nlen = 0; + char **rowpp; + + if (rowp2[i * ncols2 + namec2]) { + nlen = strlen(rowp2[i * ncols2 + namec2]); + } + if (nlen < 17 || + strncmp(rowp2[i * ncols2 + namec2], + "sqlite_autoindex_", 17)) { + continue; + } + if (*rowp2[i * ncols2 + uniquec2] != '0') { + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp2[i * ncols2 + namec2]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret == SQLITE4_OK) { + size += nnrows; + freerows(rowpp); + } + } + } + } + } + if (size == 0) { + freerows(rowp); + freerows(rowp2); + return SQL_SUCCESS; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + freerows(rowp); + freerows(rowp2); + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + offs = s->ncols; + if (rowp) { + for (i = 1; i <= nrows; i++) { + if (*rowp[i * ncols + uniquec] != '0') { + char buf[32]; + + s->rows[offs + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[offs + 1] = xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[offs + 1] = xstrdup(""); +#endif + s->rows[offs + 2] = xstrdup(tname); + s->rows[offs + 3] = xstrdup(rowp[i * ncols + namec]); + sprintf(buf, "%d", seq++); + s->rows[offs + 4] = xstrdup(buf); + offs += s->ncols; + } + } + } + if (rowp2) { + for (i = 1; i <= nrows2; i++) { + int nnrows, nncols, nlen = 0; + char **rowpp; + + if (rowp2[i * ncols2 + namec2]) { + nlen = strlen(rowp2[i * ncols2 + namec2]); + } + if (nlen < 17 || + strncmp(rowp2[i * ncols2 + namec2], "sqlite_autoindex_", 17)) { + continue; + } + if (*rowp2[i * ncols2 + uniquec2] != '0') { + int k; + + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp2[i * ncols2 + namec2]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK) { + continue; + } + for (k = 0; nnrows && k < nncols; k++) { + if (strcmp(rowpp[k], "name") == 0) { + int m; + + for (m = 1; m <= nnrows; m++) { + int roffs = offs + (m - 1) * s->ncols; + + s->rows[roffs + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[roffs + 1] = + xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[roffs + 1] = xstrdup(""); +#endif + s->rows[roffs + 2] = xstrdup(tname); + s->rows[roffs + 3] = + xstrdup(rowpp[m * nncols + k]); + s->rows[roffs + 5] = + xstrdup(rowp2[i * ncols2 + namec2]); + } + } else if (strcmp(rowpp[k], "seqno") == 0) { + int m; + + for (m = 1; m <= nnrows; m++) { + int roffs = offs + (m - 1) * s->ncols; + int pos = m - 1; + char buf[32]; + + sscanf(rowpp[m * nncols + k], "%d", &pos); + sprintf(buf, "%d", pos + 1); + s->rows[roffs + 4] = xstrdup(buf); + } + } + } + offs += nnrows * s->ncols; + freerows(rowpp); + } + } + } + freerows(rowp); + freerows(rowp2); + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve information about indexed columns. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLPrimaryKeys(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvprimarykeys(stmt, cat, catLen, schema, schemaLen, + table, tableLen); + goto done2; + } + if (cat) { + c = wmb_to_utf_c((char *) cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvprimarykeys(stmt, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS); +#else + ret = drvprimarykeys(stmt, cat, catLen, schema, schemaLen, + table, tableLen); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information about indexed columns (UNICODE version). + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLPrimaryKeysW(SQLHSTMT stmt, + SQLWCHAR *cat, SQLSMALLINT catLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen) +{ + char *c = NULL, *s = NULL, *t = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cat) { + c = uc_to_utf_c(cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvprimarykeys(stmt, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS); +done: + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLSpecialColumns(). + */ + +static COL scolSpec2[] = { + { "SYSTEM", "COLUMN", "SCOPE", SQL_SMALLINT, 1 }, + { "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "PRECISION", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "LENGTH", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "PSEUDO_COLUMN", SQL_SMALLINT, 1 }, + { "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 1 } +}; + +static COL scolSpec3[] = { + { "SYSTEM", "COLUMN", "SCOPE", SQL_SMALLINT, 1 }, + { "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "COLUMN_SIZE", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "BUFFER_LENGTH", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "PSEUDO_COLUMN", SQL_SMALLINT, 1 }, + { "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 1 } +}; + +/** + * Internal retrieve information about indexed columns. + * @param stmt statement handle + * @param id type of information, e.g. best row id + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param scope + * @param nullable + * @result ODBC error code + */ + +static SQLRETURN +drvspecialcolumns(SQLHSTMT stmt, SQLUSMALLINT id, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT scope, SQLUSMALLINT nullable) +{ + STMT *s; + DBC *d; + SQLRETURN sret; + int i, asize, ret, nrows, ncols, nnnrows, nnncols, offs; + PTRDIFF_T size; + int namec = -1, uniquec = -1, namecc = -1, typecc = -1; + int notnullcc = -1, mkrowid = 0; + char *errp = NULL, *sql, tname[512]; + char **rowp = NULL, **rowppp = NULL; + + sret = mkresultset(stmt, scolSpec2, array_size(scolSpec2), + scolSpec3, array_size(scolSpec3), &asize); + if (sret != SQL_SUCCESS) { + return sret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (!table || table[0] == '\0' || table[0] == '%') { + setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + tname[size] = '\0'; + unescpat(tname); + if (id != SQL_BEST_ROWID) { + return SQL_SUCCESS; + } + sql = sqlite4_mprintf(0, "PRAGMA index_list(%Q)", tname); + if (!sql) { + return nomem(s); + } + sret = starttran(s); + if (sret != SQL_SUCCESS) { + sqlite4_free(0, sql); + return sret; + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { +doerr: + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + size = 0; /* number result rows */ + if (ncols * nrows <= 0) { + goto nodata_but_rowid; + } + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", tname); + if (!sql) { + return nomem(s); + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowppp, &nnnrows, &nnncols, + &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + freerows(rowp); + goto doerr; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + namec = findcol(rowp, ncols, "name"); + uniquec = findcol(rowp, ncols, "unique"); + if (namec < 0 || uniquec < 0) { + goto nodata_but_rowid; + } + namecc = findcol(rowppp, nnncols, "name"); + typecc = findcol(rowppp, nnncols, "type"); + notnullcc = findcol(rowppp, nnncols, "notnull"); + for (i = 1; i <= nrows; i++) { + int nnrows, nncols; + char **rowpp = NULL; + + if (*rowp[i * ncols + uniquec] != '0') { + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp[i * ncols + namec]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret == SQLITE4_OK) { + size += nnrows; + freerows(rowpp); + } + } + } +nodata_but_rowid: + if (size == 0) { + size = 1; + mkrowid = 1; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + freerows(rowp); + freerows(rowppp); + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + if (mkrowid) { + s->nrows = 0; + goto mkrowid; + } + offs = 0; + for (i = 1; i <= nrows; i++) { + int nnrows, nncols; + char **rowpp = NULL; + + if (*rowp[i * ncols + uniquec] != '0') { + int k; + + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp[i * ncols + namec]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK) { + continue; + } + for (k = 0; nnrows && k < nncols; k++) { + if (strcmp(rowpp[k], "name") == 0) { + int m; + + for (m = 1; m <= nnrows; m++) { + int roffs = (offs + m) * s->ncols; + + s->rows[roffs + 0] = + xstrdup(stringify(SQL_SCOPE_SESSION)); + s->rows[roffs + 1] = xstrdup(rowpp[m * nncols + k]); + s->rows[roffs + 4] = xstrdup("0"); + s->rows[roffs + 7] = + xstrdup(stringify(SQL_PC_NOT_PSEUDO)); + if (namecc >= 0 && typecc >= 0) { + int ii; + + for (ii = 1; ii <= nnnrows; ii++) { + if (strcmp(rowppp[ii * nnncols + namecc], + rowpp[m * nncols + k]) == 0) { + char *typen = rowppp[ii * nnncols + typecc]; + int sqltype, mm, dd, isnullable = 0; + char buf[32]; + + s->rows[roffs + 3] = xstrdup(typen); + sqltype = mapsqltype(typen, NULL, *s->ov3, + s->nowchar[0], + s->dobigint); + getmd(typen, sqltype, &mm, &dd); +#ifdef SQL_LONGVARCHAR + if (sqltype == SQL_VARCHAR && mm > 255) { + sqltype = SQL_LONGVARCHAR; + } +#endif +#ifdef WINTERFACE +#ifdef SQL_WLONGVARCHAR + if (sqltype == SQL_WVARCHAR && mm > 255) { + sqltype = SQL_WLONGVARCHAR; + } +#endif +#endif + if (sqltype == SQL_VARBINARY && mm > 255) { + sqltype = SQL_LONGVARBINARY; + } + sprintf(buf, "%d", sqltype); + s->rows[roffs + 2] = xstrdup(buf); + sprintf(buf, "%d", mm); + s->rows[roffs + 5] = xstrdup(buf); + sprintf(buf, "%d", dd); + s->rows[roffs + 6] = xstrdup(buf); + if (notnullcc >= 0) { + char *inp = + rowppp[ii * nnncols + notnullcc]; + + isnullable = inp[0] != '0'; + } + sprintf(buf, "%d", isnullable); + s->rows[roffs + 8] = xstrdup(buf); + } + } + } + } + } + } + offs += nnrows; + freerows(rowpp); + } + } + if (nullable == SQL_NO_NULLS) { + for (i = 1; i < s->nrows; i++) { + if (s->rows[i * s->ncols + 8][0] == '0') { + int m, i1 = i + 1; + + for (m = 0; m < s->ncols; m++) { + freep(&s->rows[i * s->ncols + m]); + } + size = s->ncols * sizeof (char *) * (s->nrows - i1); + if (size > 0) { + memmove(s->rows + i * s->ncols, + s->rows + i1 * s->ncols, + size); + memset(s->rows + s->nrows * s->ncols, 0, + s->ncols * sizeof (char *)); + } + s->nrows--; + --i; + } + } + } +mkrowid: + freerows(rowp); + freerows(rowppp); + if (s->nrows == 0) { + s->rows[s->ncols + 0] = xstrdup(stringify(SQL_SCOPE_SESSION)); + s->rows[s->ncols + 1] = xstrdup("_ROWID_"); + s->rows[s->ncols + 2] = xstrdup(stringify(SQL_INTEGER)); + s->rows[s->ncols + 3] = xstrdup("integer"); + s->rows[s->ncols + 4] = xstrdup("0"); + s->rows[s->ncols + 5] = xstrdup("10"); + s->rows[s->ncols + 6] = xstrdup("9"); + s->rows[s->ncols + 7] = xstrdup(stringify(SQL_PC_PSEUDO)); + s->rows[s->ncols + 8] = xstrdup(stringify(SQL_FALSE)); + s->nrows = 1; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve information about indexed columns. + * @param stmt statement handle + * @param id type of information, e.g. best row id + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param scope + * @param nullable + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSpecialColumns(SQLHSTMT stmt, SQLUSMALLINT id, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT scope, SQLUSMALLINT nullable) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvspecialcolumns(stmt, id, cat, catLen, schema, schemaLen, + table, tableLen, scope, nullable); + goto done2; + } + if (cat) { + c = wmb_to_utf_c((char *) cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvspecialcolumns(stmt, id, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS, + scope, nullable); +#else + ret = drvspecialcolumns(stmt, id, cat, catLen, schema, schemaLen, + table, tableLen, scope, nullable); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information about indexed columns (UNICODE version). + * @param stmt statement handle + * @param id type of information, e.g. best row id + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param scope + * @param nullable + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSpecialColumnsW(SQLHSTMT stmt, SQLUSMALLINT id, + SQLWCHAR *cat, SQLSMALLINT catLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT scope, SQLUSMALLINT nullable) +{ + char *c = NULL, *s = NULL, *t = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cat) { + c = uc_to_utf_c(cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvspecialcolumns(stmt, id, (SQLCHAR *) c, SQL_NTS, + (SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS, + scope, nullable); +done: + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLForeignKeys(). + */ + +static COL fkeySpec2[] = { + { "SYSTEM", "FOREIGNKEY", "PKTABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "PKTABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "PKTABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "PKCOLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "FKCOLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "KEY_SEQ", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "UPDATE_RULE", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "DELETE_RULE", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "FK_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "PK_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "DEFERRABILITY", SQL_SMALLINT, 5 } +}; + +static COL fkeySpec3[] = { + { "SYSTEM", "FOREIGNKEY", "PKTABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "PKTABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "PKTABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "PKCOLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "FOREIGNKEY", "FKTABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "FKCOLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "KEY_SEQ", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "UPDATE_RULE", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "DELETE_RULE", SQL_SMALLINT, 5 }, + { "SYSTEM", "FOREIGNKEY", "FK_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "PK_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "FOREIGNKEY", "DEFERRABILITY", SQL_SMALLINT, 5 } +}; + +/** + * Internal retrieve information about primary/foreign keys. + * @param stmt statement handle + * @param PKcatalog primary key catalog name/pattern or NULL + * @param PKcatalogLen length of PKcatalog or SQL_NTS + * @param PKschema primary key schema name/pattern or NULL + * @param PKschemaLen length of PKschema or SQL_NTS + * @param PKtable primary key table name/pattern or NULL + * @param PKtableLen length of PKtable or SQL_NTS + * @param FKcatalog foreign key catalog name/pattern or NULL + * @param FKcatalogLen length of FKcatalog or SQL_NTS + * @param FKschema foreign key schema name/pattern or NULL + * @param FKschemaLen length of FKschema or SQL_NTS + * @param FKtable foreign key table name/pattern or NULL + * @param FKtableLen length of FKtable or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN SQL_API +drvforeignkeys(SQLHSTMT stmt, + SQLCHAR *PKcatalog, SQLSMALLINT PKcatalogLen, + SQLCHAR *PKschema, SQLSMALLINT PKschemaLen, + SQLCHAR *PKtable, SQLSMALLINT PKtableLen, + SQLCHAR *FKcatalog, SQLSMALLINT FKcatalogLen, + SQLCHAR *FKschema, SQLSMALLINT FKschemaLen, + SQLCHAR *FKtable, SQLSMALLINT FKtableLen) +{ + STMT *s; + DBC *d; + SQLRETURN sret; + int i, asize, ret, nrows, ncols, offs, namec, seqc, fromc, toc; + int onu, ond; + PTRDIFF_T size; + char **rowp, *errp = NULL, *sql, pname[512], fname[512]; + + sret = mkresultset(stmt, fkeySpec2, array_size(fkeySpec2), + fkeySpec3, array_size(fkeySpec3), &asize); + if (sret != SQL_SUCCESS) { + return sret; + } + s = (STMT *) stmt; + sret = starttran(s); + if (sret != SQL_SUCCESS) { + return sret; + } + d = (DBC *) s->dbc; + if ((!PKtable || PKtable[0] == '\0' || PKtable[0] == '%') && + (!FKtable || FKtable[0] == '\0' || FKtable[0] == '%')) { + setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + size = 0; + if (PKtable) { + if (PKtableLen == SQL_NTS) { + size = sizeof (pname) - 1; + } else { + size = min(sizeof (pname) - 1, PKtableLen); + } + strncpy(pname, (char *) PKtable, size); + } + pname[size] = '\0'; + size = 0; + if (FKtable) { + + if (FKtableLen == SQL_NTS) { + size = sizeof (fname) - 1; + } else { + size = min(sizeof (fname) - 1, FKtableLen); + } + strncpy(fname, (char *) FKtable, size); + } + fname[size] = '\0'; + if (fname[0] != '\0') { + int plen; + + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA foreign_key_list(%Q)", fname); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, + &nrows, &ncols, &errp); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + if (ncols * nrows <= 0) { +nodata: + freerows(rowp); + return SQL_SUCCESS; + } + size = 0; + namec = findcol(rowp, ncols, "table"); + seqc = findcol(rowp, ncols, "seq"); + fromc = findcol(rowp, ncols, "from"); + toc = findcol(rowp, ncols, "to"); + onu = findcol(rowp, ncols, "on_update"); + ond = findcol(rowp, ncols, "on_delete"); + if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) { + goto nodata; + } + plen = strlen(pname); + for (i = 1; i <= nrows; i++) { + char *ptab = unquote(rowp[i * ncols + namec]); + + if (plen && ptab) { + int len = strlen(ptab); + + if (plen != len || strncasecmp(pname, ptab, plen) != 0) { + continue; + } + } + size++; + } + if (size == 0) { + goto nodata; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + offs = 0; + for (i = 1; i <= nrows; i++) { + int pos = 0, roffs = (offs + 1) * s->ncols; + char *ptab = rowp[i * ncols + namec]; + char buf[32]; + + if (plen && ptab) { + int len = strlen(ptab); + + if (plen != len || strncasecmp(pname, ptab, plen) != 0) { + continue; + } + } + s->rows[roffs + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[roffs + 1] = xstrdup(""); +#endif + s->rows[roffs + 2] = xstrdup(ptab); + s->rows[roffs + 3] = xstrdup(rowp[i * ncols + toc]); + s->rows[roffs + 4] = xstrdup(""); + s->rows[roffs + 5] = xstrdup(""); + s->rows[roffs + 6] = xstrdup(fname); + s->rows[roffs + 7] = xstrdup(rowp[i * ncols + fromc]); + sscanf(rowp[i * ncols + seqc], "%d", &pos); + sprintf(buf, "%d", pos + 1); + s->rows[roffs + 8] = xstrdup(buf); + if (onu < 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); + } else { + if (strcmp(rowp[i * ncols + onu], "SET NULL") == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_NULL)); + } else if (strcmp(rowp[i * ncols + onu], "SET DEFAULT") == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_DEFAULT)); + } else if (strcmp(rowp[i * ncols + onu], "CASCADE") == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_CASCADE)); + } else if (strcmp(rowp[i * ncols + onu], "RESTRICT") == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_RESTRICT)); + } else { + s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); + } + } + if (ond < 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION)); + } else { + if (strcmp(rowp[i * ncols + ond], "SET NULL") == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_NULL)); + } else if (strcmp(rowp[i * ncols + ond], "SET DEFAULT") == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_DEFAULT)); + } else if (strcmp(rowp[i * ncols + ond], "CASCADE") == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_CASCADE)); + } else if (strcmp(rowp[i * ncols + ond], "RESTRICT") == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_RESTRICT)); + } else { + s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION)); + } + } + s->rows[roffs + 11] = NULL; + s->rows[roffs + 12] = NULL; + s->rows[roffs + 13] = xstrdup(stringify(SQL_NOT_DEFERRABLE)); + offs++; + } + freerows(rowp); + } else { + int nnrows, nncols, plen = strlen(pname); + char **rowpp; + + sql = "select name from sqlite_master where type='table'"; + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + if (ncols * nrows <= 0) { + goto nodata; + } + size = 0; + for (i = 1; i <= nrows; i++) { + int k; + + if (!rowp[i]) { + continue; + } + rowpp = NULL; + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA foreign_key_list(%Q)", rowp[i]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK || nncols * nnrows <= 0) { + freerows(rowpp); + continue; + } + namec = findcol(rowpp, nncols, "table"); + seqc = findcol(rowpp, nncols, "seq"); + fromc = findcol(rowpp, nncols, "from"); + toc = findcol(rowpp, nncols, "to"); + if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) { + freerows(rowpp); + continue; + } + for (k = 1; k <= nnrows; k++) { + char *ptab = unquote(rowpp[k * nncols + namec]); + + if (plen && ptab) { + int len = strlen(ptab); + + if (len != plen || strncasecmp(pname, ptab, plen) != 0) { + continue; + } + } + size++; + } + freerows(rowpp); + } + if (size == 0) { + goto nodata; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + offs = 0; + for (i = 1; i <= nrows; i++) { + int k; + + if (!rowp[i]) { + continue; + } + rowpp = NULL; + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA foreign_key_list(%Q)", rowp[i]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK || nncols * nnrows <= 0) { + freerows(rowpp); + continue; + } + namec = findcol(rowpp, nncols, "table"); + seqc = findcol(rowpp, nncols, "seq"); + fromc = findcol(rowpp, nncols, "from"); + toc = findcol(rowpp, nncols, "to"); + onu = findcol(rowpp, nncols, "on_update"); + ond = findcol(rowpp, nncols, "on_delete"); + if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) { + freerows(rowpp); + continue; + } + for (k = 1; k <= nnrows; k++) { + int pos = 0, roffs = (offs + 1) * s->ncols; + char *ptab = unquote(rowpp[k * nncols + namec]); + char buf[32]; + + if (plen && ptab) { + int len = strlen(ptab); + + if (len != plen || strncasecmp(pname, ptab, plen) != 0) { + continue; + } + } + s->rows[roffs + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[roffs + 1] = xstrdup(""); +#endif + s->rows[roffs + 2] = xstrdup(ptab); + s->rows[roffs + 3] = xstrdup(rowpp[k * nncols + toc]); + s->rows[roffs + 4] = xstrdup(""); + s->rows[roffs + 5] = xstrdup(""); + s->rows[roffs + 6] = xstrdup(rowp[i]); + s->rows[roffs + 7] = xstrdup(rowpp[k * nncols + fromc]); + sscanf(rowpp[k * nncols + seqc], "%d", &pos); + sprintf(buf, "%d", pos + 1); + s->rows[roffs + 8] = xstrdup(buf); + if (onu < 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); + } else { + if (strcmp(rowpp[k * nncols + onu], "SET NULL") == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_NULL)); + } else if (strcmp(rowpp[k * nncols + onu], "SET DEFAULT") + == 0) { + s->rows[roffs + 9] = + xstrdup(stringify(SQL_SET_DEFAULT)); + } else if (strcmp(rowpp[k * nncols + onu], "CASCADE") + == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_CASCADE)); + } else if (strcmp(rowpp[k * nncols + onu], "RESTRICT") + == 0) { + s->rows[roffs + 9] = xstrdup(stringify(SQL_RESTRICT)); + } else { + s->rows[roffs + 9] = + xstrdup(stringify(SQL_NO_ACTION)); + } + } + if (ond < 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION)); + } else { + if (strcmp(rowpp[k * nncols + ond], "SET NULL") == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_NULL)); + } else if (strcmp(rowpp[k * nncols + ond], "SET DEFAULT") + == 0) { + s->rows[roffs + 10] = + xstrdup(stringify(SQL_SET_DEFAULT)); + } else if (strcmp(rowpp[k * nncols + ond], "CASCADE") + == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_CASCADE)); + } else if (strcmp(rowpp[k * nncols + ond], "RESTRICT") + == 0) { + s->rows[roffs + 10] = xstrdup(stringify(SQL_RESTRICT)); + } else { + s->rows[roffs + 10] = + xstrdup(stringify(SQL_NO_ACTION)); + } + } + s->rows[roffs + 11] = NULL; + s->rows[roffs + 12] = NULL; + s->rows[roffs + 13] = xstrdup(stringify(SQL_NOT_DEFERRABLE)); + offs++; + } + freerows(rowpp); + } + freerows(rowp); + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve information about primary/foreign keys. + * @param stmt statement handle + * @param PKcatalog primary key catalog name/pattern or NULL + * @param PKcatalogLen length of PKcatalog or SQL_NTS + * @param PKschema primary key schema name/pattern or NULL + * @param PKschemaLen length of PKschema or SQL_NTS + * @param PKtable primary key table name/pattern or NULL + * @param PKtableLen length of PKtable or SQL_NTS + * @param FKcatalog foreign key catalog name/pattern or NULL + * @param FKcatalogLen length of FKcatalog or SQL_NTS + * @param FKschema foreign key schema name/pattern or NULL + * @param FKschemaLen length of FKschema or SQL_NTS + * @param FKtable foreign key table name/pattern or NULL + * @param FKtableLen length of FKtable or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLForeignKeys(SQLHSTMT stmt, + SQLCHAR *PKcatalog, SQLSMALLINT PKcatalogLen, + SQLCHAR *PKschema, SQLSMALLINT PKschemaLen, + SQLCHAR *PKtable, SQLSMALLINT PKtableLen, + SQLCHAR *FKcatalog, SQLSMALLINT FKcatalogLen, + SQLCHAR *FKschema, SQLSMALLINT FKschemaLen, + SQLCHAR *FKtable, SQLSMALLINT FKtableLen) +{ +#if defined(_WIN32) || defined(_WIN64) + char *pc = NULL, *ps = NULL, *pt = NULL; + char *fc = NULL, *fs = NULL, *ft = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvforeignkeys(stmt, + PKcatalog, PKcatalogLen, + PKschema, PKschemaLen, PKtable, PKtableLen, + FKcatalog, FKcatalogLen, + FKschema, FKschemaLen, + FKtable, FKtableLen); + goto done2; + } + if (PKcatalog) { + pc = wmb_to_utf_c((char *) PKcatalog, PKcatalogLen); + if (!pc) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (PKschema) { + ps = wmb_to_utf_c((char *) PKschema, PKschemaLen); + if (!ps) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (PKtable) { + pt = wmb_to_utf_c((char *) PKtable, PKtableLen); + if (!pt) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKcatalog) { + fc = wmb_to_utf_c((char *) FKcatalog, FKcatalogLen); + if (!fc) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKschema) { + fs = wmb_to_utf_c((char *) FKschema, FKschemaLen); + if (!fs) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKtable) { + ft = wmb_to_utf_c((char *) FKtable, FKtableLen); + if (!ft) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvforeignkeys(stmt, (SQLCHAR *) pc, SQL_NTS, + (SQLCHAR *) ps, SQL_NTS, (SQLCHAR *) pt, SQL_NTS, + (SQLCHAR *) fc, SQL_NTS, (SQLCHAR *) fs, SQL_NTS, + (SQLCHAR *) ft, SQL_NTS); +#else + ret = drvforeignkeys(stmt, + PKcatalog, PKcatalogLen, + PKschema, PKschemaLen, PKtable, PKtableLen, + FKcatalog, FKcatalogLen, + FKschema, FKschemaLen, + FKtable, FKtableLen); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(ft); + uc_free(fs); + uc_free(fc); + uc_free(pt); + uc_free(ps); + uc_free(pc); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information about primary/foreign keys (UNICODE version). + * @param stmt statement handle + * @param PKcatalog primary key catalog name/pattern or NULL + * @param PKcatalogLen length of PKcatalog or SQL_NTS + * @param PKschema primary key schema name/pattern or NULL + * @param PKschemaLen length of PKschema or SQL_NTS + * @param PKtable primary key table name/pattern or NULL + * @param PKtableLen length of PKtable or SQL_NTS + * @param FKcatalog foreign key catalog name/pattern or NULL + * @param FKcatalogLen length of FKcatalog or SQL_NTS + * @param FKschema foreign key schema name/pattern or NULL + * @param FKschemaLen length of FKschema or SQL_NTS + * @param FKtable foreign key table name/pattern or NULL + * @param FKtableLen length of FKtable or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLForeignKeysW(SQLHSTMT stmt, + SQLWCHAR *PKcatalog, SQLSMALLINT PKcatalogLen, + SQLWCHAR *PKschema, SQLSMALLINT PKschemaLen, + SQLWCHAR *PKtable, SQLSMALLINT PKtableLen, + SQLWCHAR *FKcatalog, SQLSMALLINT FKcatalogLen, + SQLWCHAR *FKschema, SQLSMALLINT FKschemaLen, + SQLWCHAR *FKtable, SQLSMALLINT FKtableLen) +{ + char *pc = NULL, *ps = NULL, *pt = NULL; + char *fc = NULL, *fs = NULL, *ft = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (PKcatalog) { + pc = uc_to_utf_c(PKcatalog, PKcatalogLen); + if (!pc) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (PKschema) { + ps = uc_to_utf_c(PKschema, PKschemaLen); + if (!ps) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (PKtable) { + pt = uc_to_utf_c(PKtable, PKtableLen); + if (!pt) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKcatalog) { + fc = uc_to_utf_c(FKcatalog, FKcatalogLen); + if (!fc) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKschema) { + fs = uc_to_utf_c(FKschema, FKschemaLen); + if (!fs) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (FKtable) { + ft = uc_to_utf_c(FKtable, FKtableLen); + if (!ft) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvforeignkeys(stmt, (SQLCHAR *) pc, SQL_NTS, + (SQLCHAR *) ps, SQL_NTS, (SQLCHAR *) pt, SQL_NTS, + (SQLCHAR *) fc, SQL_NTS, (SQLCHAR *) fs, SQL_NTS, + (SQLCHAR *) ft, SQL_NTS); +done: + uc_free(ft); + uc_free(fs); + uc_free(fc); + uc_free(pt); + uc_free(ps); + uc_free(pc); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Start transaction when autocommit off + * @param s statement pointer + * @result ODBC error code + */ + +static SQLRETURN +starttran(STMT *s) +{ + int ret = SQL_SUCCESS, rc; + char *errp = NULL; + DBC *d = (DBC *) s->dbc; + + if (!d->autocommit && !d->intrans && !d->trans_disable) { + rc = sqlite4_exec(d->sqlite, "BEGIN TRANSACTION", NULL, NULL); + dbtracerc(d, rc, NULL); + if (rc != SQLITE4_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", rc); + ret = SQL_ERROR; + } else { + d->intrans = 1; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + } + return ret; +} + +/** + * Internal commit or rollback transaction. + * @param d database connection pointer + * @param comptype type of transaction's end, SQL_COMMIT or SQL_ROLLBACK + * @param force force action regardless of DBC's autocommit state + * @result ODBC error code + */ + +static SQLRETURN +endtran(DBC *d, SQLSMALLINT comptype, int force) +{ + int ret; + char *sql, *errp = NULL; + + if (!d->sqlite) { + setstatd(d, -1, "not connected", (*d->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if ((!force && d->autocommit) || !d->intrans) { + return SQL_SUCCESS; + } + switch (comptype) { + case SQL_COMMIT: + sql = "COMMIT TRANSACTION"; + goto doit; + case SQL_ROLLBACK: + sql = "ROLLBACK TRANSACTION"; + doit: + ret = sqlite4_exec(d->sqlite, sql, NULL, NULL); + dbtracerc(d, ret, NULL); + d->intrans = 0; + if (ret != SQLITE4_OK) { + setstatd(d, ret, "%s", (*d->ov3) ? "HY000" : "S1000", + errp ? errp : "transaction failed"); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_SUCCESS; + } + setstatd(d, -1, "invalid completion type", (*d->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; +} + +/** + * Internal commit or rollback transaction. + * @param type type of handle + * @param handle HDBC, HENV, or HSTMT handle + * @param comptype SQL_COMMIT or SQL_ROLLBACK + * @result ODBC error code + */ + +static SQLRETURN +drvendtran(SQLSMALLINT type, SQLHANDLE handle, SQLSMALLINT comptype) +{ + DBC *d; + int fail = 0; + SQLRETURN ret; +#if defined(_WIN32) || defined(_WIN64) + ENV *e; +#endif + + switch (type) { + case SQL_HANDLE_DBC: + HDBC_LOCK((SQLHDBC) handle); + if (handle == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) handle; + ret = endtran(d, comptype, 0); + HDBC_UNLOCK((SQLHDBC) handle); + return ret; + case SQL_HANDLE_ENV: + if (handle == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } +#if defined(_WIN32) || defined(_WIN64) + e = (ENV *) handle; + if (e->magic != ENV_MAGIC) { + return SQL_INVALID_HANDLE; + } + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); +#endif + d = ((ENV *) handle)->dbcs; + while (d) { + ret = endtran(d, comptype, 0); + if (ret != SQL_SUCCESS) { + fail++; + } + d = d->next; + } +#if defined(_WIN32) || defined(_WIN64) + e->owner = 0; + LeaveCriticalSection(&e->cs); +#endif + return fail ? SQL_ERROR : SQL_SUCCESS; + } + return SQL_INVALID_HANDLE; +} + +/** + * Commit or rollback transaction. + * @param type type of handle + * @param handle HDBC, HENV, or HSTMT handle + * @param comptype SQL_COMMIT or SQL_ROLLBACK + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLEndTran(SQLSMALLINT type, SQLHANDLE handle, SQLSMALLINT comptype) +{ + return drvendtran(type, handle, comptype); +} + +/** + * Commit or rollback transaction. + * @param env environment handle or NULL + * @param dbc database connection handle or NULL + * @param type SQL_COMMIT or SQL_ROLLBACK + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLTransact(SQLHENV env, SQLHDBC dbc, SQLUSMALLINT type) +{ + if (env != SQL_NULL_HENV) { + return drvendtran(SQL_HANDLE_ENV, (SQLHANDLE) env, type); + } + return drvendtran(SQL_HANDLE_DBC, (SQLHANDLE) dbc, type); +} + +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLCopyDesc(SQLHDESC source, SQLHDESC target) +{ + return SQL_ERROR; +} + +#ifndef WINTERFACE +/** + * Translate SQL string. + * @param stmt statement handle + * @param sqlin input string + * @param sqlinLen length of input string + * @param sql output string + * @param sqlMax max space in output string + * @param sqlLen value return for length of output string + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLNativeSql(SQLHSTMT stmt, SQLCHAR *sqlin, SQLINTEGER sqlinLen, + SQLCHAR *sql, SQLINTEGER sqlMax, SQLINTEGER *sqlLen) +{ + int outLen = 0; + SQLRETURN ret = SQL_SUCCESS; + + HSTMT_LOCK(stmt); + if (sqlinLen == SQL_NTS) { + sqlinLen = strlen((char *) sqlin); + } + if (sql) { + if (sqlMax > 0) { + strncpy((char *) sql, (char *) sqlin, sqlMax - 1); + sqlin[sqlMax - 1] = '\0'; + outLen = min(sqlMax - 1, sqlinLen); + } + } else { + outLen = sqlinLen; + } + if (sqlLen) { + *sqlLen = outLen; + } + if (sql && outLen < sqlinLen) { + setstat((STMT *) stmt, -1, "data right truncated", "01004"); + ret = SQL_SUCCESS_WITH_INFO; + } + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Translate SQL string (UNICODE version). + * @param stmt statement handle + * @param sqlin input string + * @param sqlinLen length of input string + * @param sql output string + * @param sqlMax max space in output string + * @param sqlLen value return for length of output string + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLNativeSqlW(SQLHSTMT stmt, SQLWCHAR *sqlin, SQLINTEGER sqlinLen, + SQLWCHAR *sql, SQLINTEGER sqlMax, SQLINTEGER *sqlLen) +{ + int outLen = 0; + SQLRETURN ret = SQL_SUCCESS; + + HSTMT_LOCK(stmt); + if (sqlinLen == SQL_NTS) { + sqlinLen = uc_strlen(sqlin); + } + if (sql) { + if (sqlMax > 0) { + uc_strncpy(sql, sqlin, sqlMax - 1); + sqlin[sqlMax - 1] = 0; + outLen = min(sqlMax - 1, sqlinLen); + } + } else { + outLen = sqlinLen; + } + if (sqlLen) { + *sqlLen = outLen; + } + if (sql && outLen < sqlinLen) { + setstat((STMT *) stmt, -1, "data right truncated", "01004"); + ret = SQL_SUCCESS_WITH_INFO; + } + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLProcedures(). + */ + +static COL procSpec2[] = { + { "SYSTEM", "PROCEDURE", "PROCEDURE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCEDURE", "NUM_INPUT_PARAMS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "NUM_OUTPUT_PARAMS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "NUM_RESULT_SETS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "REMARKS", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_TYPE", SQL_SMALLINT, 5 } +}; + +static COL procSpec3[] = { + { "SYSTEM", "PROCEDURE", "PROCEDURE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCEDURE", "NUM_INPUT_PARAMS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "NUM_OUTPUT_PARAMS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "NUM_RESULT_SETS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCEDURE", "REMARKS", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCEDURE", "PROCEDURE_TYPE", SQL_SMALLINT, 5 } +}; + +#ifndef WINTERFACE +/** + * Retrieve information about stored procedures. + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema or SQL_NTS + * @param proc procedure name/pattern or NULL + * @param procLen length of proc or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLProcedures(SQLHSTMT stmt, + SQLCHAR *catalog, SQLSMALLINT catalogLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *proc, SQLSMALLINT procLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, procSpec2, array_size(procSpec2), + procSpec3, array_size(procSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information about stored procedures (UNICODE version). + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema or SQL_NTS + * @param proc procedure name/pattern or NULL + * @param procLen length of proc or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLProceduresW(SQLHSTMT stmt, + SQLWCHAR *catalog, SQLSMALLINT catalogLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *proc, SQLSMALLINT procLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, procSpec2, array_size(procSpec2), + procSpec3, array_size(procSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLProcedureColumns(). + */ + +static COL procColSpec2[] = { + { "SYSTEM", "PROCCOL", "PROCEDURE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "PROCEDURE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "PROCEDURE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCCOL", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCCOL", "COLUMN_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "DATA_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "PRECISION", SQL_INTEGER, 10 }, + { "SYSTEM", "PROCCOL", "LENGTH", SQL_INTEGER, 10 }, + { "SYSTEM", "PROCCOL", "SCALE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "RADIX", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "NULLABLE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "REMARKS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "COLUMN_DEF", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "SQL_DATA_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "SQL_DATETIME_SUB", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "ORDINAL_POSITION", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "IS_NULLABLE", SCOL_VARCHAR, 50 } +}; + +static COL procColSpec3[] = { + { "SYSTEM", "PROCCOL", "PROCEDURE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "PROCEDURE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "PROCEDURE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCCOL", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "PROCCOL", "COLUMN_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "DATA_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "COLUMN_SIZE", SQL_INTEGER, 10 }, + { "SYSTEM", "PROCCOL", "BUFFER_LENGTH", SQL_INTEGER, 10 }, + { "SYSTEM", "PROCCOL", "DECIMAL_DIGITS", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "NUM_PREC_RADIX", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "NULLABLE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "REMARKS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "COLUMN_DEF", SCOL_VARCHAR, 50 }, + { "SYSTEM", "PROCCOL", "SQL_DATA_TYPE", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "SQL_DATETIME_SUB", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "ORDINAL_POSITION", SQL_SMALLINT, 5 }, + { "SYSTEM", "PROCCOL", "IS_NULLABLE", SCOL_VARCHAR, 50 } +}; + +#ifndef WINTERFACE +/** + * Retrieve information about columns in result set of stored procedures. + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema or SQL_NTS + * @param proc procedure name/pattern or NULL + * @param procLen length of proc or SQL_NTS + * @param column column name/pattern or NULL + * @param columnLen length of column or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLProcedureColumns(SQLHSTMT stmt, + SQLCHAR *catalog, SQLSMALLINT catalogLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *proc, SQLSMALLINT procLen, + SQLCHAR *column, SQLSMALLINT columnLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, procColSpec2, array_size(procColSpec2), + procColSpec3, array_size(procColSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information about columns in result + * set of stored procedures (UNICODE version). + * @param stmt statement handle + * @param catalog catalog name/pattern or NULL + * @param catalogLen length of catalog or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema or SQL_NTS + * @param proc procedure name/pattern or NULL + * @param procLen length of proc or SQL_NTS + * @param column column name/pattern or NULL + * @param columnLen length of column or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLProcedureColumnsW(SQLHSTMT stmt, + SQLWCHAR *catalog, SQLSMALLINT catalogLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *proc, SQLSMALLINT procLen, + SQLWCHAR *column, SQLSMALLINT columnLen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = mkresultset(stmt, procColSpec2, array_size(procColSpec2), + procColSpec3, array_size(procColSpec3), NULL); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Get information of HENV. + * @param env environment handle + * @param attr attribute to be retrieved + * @param val output buffer + * @param len length of output buffer + * @param lenp output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER len, SQLINTEGER *lenp) +{ + ENV *e; + SQLRETURN ret = SQL_ERROR; + + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + e = (ENV *) env; + if (!e || e->magic != ENV_MAGIC) { + return SQL_INVALID_HANDLE; + } +#if defined(_WIN32) || defined(_WIN64) + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); +#endif + switch (attr) { + case SQL_ATTR_CONNECTION_POOLING: + ret = SQL_ERROR; + break; + case SQL_ATTR_CP_MATCH: + ret = SQL_NO_DATA; + break; + case SQL_ATTR_OUTPUT_NTS: + if (val) { + *((SQLINTEGER *) val) = SQL_TRUE; + } + if (lenp) { + *lenp = sizeof (SQLINTEGER); + } + ret = SQL_SUCCESS; + break; + case SQL_ATTR_ODBC_VERSION: + if (val) { + *((SQLINTEGER *) val) = e->ov3 ? SQL_OV_ODBC3 : SQL_OV_ODBC2; + } + if (lenp) { + *lenp = sizeof (SQLINTEGER); + } + ret = SQL_SUCCESS; + break; + } +#if defined(_WIN32) || defined(_WIN64) + e->owner = 0; + LeaveCriticalSection(&e->cs); +#endif + return ret; +} + +/** + * Set information in HENV. + * @param env environment handle + * @param attr attribute to be retrieved + * @param val parameter buffer + * @param len length of parameter + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER val, SQLINTEGER len) +{ + ENV *e; + SQLRETURN ret = SQL_ERROR; + + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + e = (ENV *) env; + if (!e || e->magic != ENV_MAGIC) { + return SQL_INVALID_HANDLE; + } +#if defined(_WIN32) || defined(_WIN64) + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); +#endif + switch (attr) { + case SQL_ATTR_CONNECTION_POOLING: + ret = SQL_SUCCESS; + break; + case SQL_ATTR_CP_MATCH: + ret = SQL_NO_DATA; + break; + case SQL_ATTR_OUTPUT_NTS: + if (val == (SQLPOINTER) SQL_TRUE) { + ret = SQL_SUCCESS; + } + break; + case SQL_ATTR_ODBC_VERSION: + if (!val) { + break; + } + if (val == (SQLPOINTER) SQL_OV_ODBC2) { + e->ov3 = 0; + ret = SQL_SUCCESS; + } + if (val == (SQLPOINTER) SQL_OV_ODBC3) { + e->ov3 = 1; + ret = SQL_SUCCESS; + } + break; + } +#if defined(_WIN32) || defined(_WIN64) + e->owner = 0; + LeaveCriticalSection(&e->cs); +#endif + return ret; +} + +/** + * Internal get error message given handle (HENV, HDBC, or HSTMT). + * @param htype handle type + * @param handle HENV, HDBC, or HSTMT + * @param recno + * @param sqlstate output buffer for SQL state + * @param nativeerr output buffer of native error code + * @param msg output buffer for error message + * @param buflen length of output buffer + * @param msglen output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetdiagrec(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLCHAR *sqlstate, SQLINTEGER *nativeerr, SQLCHAR *msg, + SQLSMALLINT buflen, SQLSMALLINT *msglen) +{ + DBC *d = NULL; + STMT *s = NULL; + int len, naterr; + char *logmsg, *sqlst; + SQLRETURN ret = SQL_ERROR; + + if (handle == SQL_NULL_HANDLE) { + return SQL_INVALID_HANDLE; + } + if (sqlstate) { + sqlstate[0] = '\0'; + } + if (msg && buflen > 0) { + msg[0] = '\0'; + } + if (msglen) { + *msglen = 0; + } + if (nativeerr) { + *nativeerr = 0; + } + switch (htype) { + case SQL_HANDLE_ENV: + case SQL_HANDLE_DESC: + return SQL_NO_DATA; + case SQL_HANDLE_DBC: + HDBC_LOCK((SQLHDBC) handle); + d = (DBC *) handle; + logmsg = (char *) d->logmsg; + sqlst = d->sqlstate; + naterr = d->naterr; + break; + case SQL_HANDLE_STMT: + HSTMT_LOCK((SQLHSTMT) handle); + s = (STMT *) handle; + logmsg = (char *) s->logmsg; + sqlst = s->sqlstate; + naterr = s->naterr; + break; + default: + return SQL_INVALID_HANDLE; + } + if (buflen < 0) { + goto done; + } + if (recno > 1) { + ret = SQL_NO_DATA; + goto done; + } + len = strlen(logmsg); + if (len == 0) { + ret = SQL_NO_DATA; + goto done; + } + if (nativeerr) { + *nativeerr = naterr; + } + if (sqlstate) { + strcpy((char *) sqlstate, sqlst); + } + if (msglen) { + *msglen = len; + } + if (len >= buflen) { + if (msg && buflen > 0) { + strncpy((char *) msg, logmsg, buflen); + msg[buflen - 1] = '\0'; + logmsg[0] = '\0'; + } + } else if (msg) { + strcpy((char *) msg, logmsg); + logmsg[0] = '\0'; + } + ret = SQL_SUCCESS; +done: + switch (htype) { + case SQL_HANDLE_DBC: + HDBC_UNLOCK((SQLHDBC) handle); + break; + case SQL_HANDLE_STMT: + HSTMT_UNLOCK((SQLHSTMT) handle); + break; + } + return ret; +} + +#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) +/** + * Get error message given handle (HENV, HDBC, or HSTMT). + * @param htype handle type + * @param handle HENV, HDBC, or HSTMT + * @param recno + * @param sqlstate output buffer for SQL state + * @param nativeerr output buffer of native error code + * @param msg output buffer for error message + * @param buflen length of output buffer + * @param msglen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetDiagRec(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLCHAR *sqlstate, SQLINTEGER *nativeerr, SQLCHAR *msg, + SQLSMALLINT buflen, SQLSMALLINT *msglen) +{ + return drvgetdiagrec(htype, handle, recno, sqlstate, + nativeerr, msg, buflen, msglen); +} +#endif + +#if !defined(HAVE_UNIXODBC) || !(HAVE_UNIXODBC) +#ifdef WINTERFACE +/** + * Get error message given handle (HENV, HDBC, or HSTMT) + * (UNICODE version). + * @param htype handle type + * @param handle HENV, HDBC, or HSTMT + * @param recno + * @param sqlstate output buffer for SQL state + * @param nativeerr output buffer of native error code + * @param msg output buffer for error message + * @param buflen length of output buffer + * @param msglen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetDiagRecW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLWCHAR *sqlstate, SQLINTEGER *nativeerr, SQLWCHAR *msg, + SQLSMALLINT buflen, SQLSMALLINT *msglen) +{ + char state[16]; + SQLSMALLINT len; + SQLRETURN ret; + + ret = drvgetdiagrec(htype, handle, recno, (SQLCHAR *) state, + nativeerr, (SQLCHAR *) msg, buflen, &len); + if (ret == SQL_SUCCESS) { + if (sqlstate) { + uc_from_utf_buf((SQLCHAR *) state, -1, sqlstate, + 6 * sizeof (SQLWCHAR)); + } + if (msg) { + if (len > 0) { + SQLWCHAR *m = NULL; + + m = uc_from_utf((unsigned char *) msg, len); + if (m) { + if (buflen) { + buflen /= sizeof (SQLWCHAR); + uc_strncpy(msg, m, buflen); + m[len] = 0; + len = min(buflen, uc_strlen(m)); + } else { + len = uc_strlen(m); + } + uc_free(m); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (buflen > 0) { + msg[0] = 0; + } + } + } else { + /* estimated length !!! */ + len *= sizeof (SQLWCHAR); + } + if (msglen) { + *msglen = len; + } + } else if (ret == SQL_NO_DATA) { + if (sqlstate) { + sqlstate[0] = 0; + } + if (msg) { + if (buflen > 0) { + msg[0] = 0; + } + } + if (msglen) { + *msglen = 0; + } + } + return ret; +} +#endif +#endif + +/** + * Get error record given handle (HDBC or HSTMT). + * @param htype handle type + * @param handle HDBC or HSTMT + * @param recno diag record number for which info to be retrieved + * @param id diag id for which info to be retrieved + * @param info output buffer for error message + * @param buflen length of output buffer + * @param stringlen output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLSMALLINT id, SQLPOINTER info, + SQLSMALLINT buflen, SQLSMALLINT *stringlen) +{ + DBC *d = NULL; + STMT *s = NULL; + int len, naterr, strbuf = 1; + char *logmsg, *sqlst, *clrmsg = NULL; + SQLRETURN ret = SQL_ERROR; + + if (handle == SQL_NULL_HANDLE) { + return SQL_INVALID_HANDLE; + } + if (stringlen) { + *stringlen = 0; + } + switch (htype) { + case SQL_HANDLE_ENV: + case SQL_HANDLE_DESC: + return SQL_NO_DATA; + case SQL_HANDLE_DBC: + HDBC_LOCK((SQLHDBC) handle); + d = (DBC *) handle; + logmsg = (char *) d->logmsg; + sqlst = d->sqlstate; + naterr = d->naterr; + break; + case SQL_HANDLE_STMT: + HSTMT_LOCK((SQLHSTMT) handle); + s = (STMT *) handle; + d = (DBC *) s->dbc; + logmsg = (char *) s->logmsg; + sqlst = s->sqlstate; + naterr = s->naterr; + break; + default: + return SQL_INVALID_HANDLE; + } + if (buflen < 0) { + switch (buflen) { + case SQL_IS_POINTER: + case SQL_IS_UINTEGER: + case SQL_IS_INTEGER: + case SQL_IS_USMALLINT: + case SQL_IS_SMALLINT: + strbuf = 0; + break; + default: + ret = SQL_ERROR; + goto done; + } + } + if (recno > 1) { + ret = SQL_NO_DATA; + goto done; + } + switch (id) { + case SQL_DIAG_CLASS_ORIGIN: + logmsg = "ISO 9075"; + if (sqlst[0] == 'I' && sqlst[1] == 'M') { + logmsg = "ODBC 3.0"; + } + break; + case SQL_DIAG_SUBCLASS_ORIGIN: + logmsg = "ISO 9075"; + if (sqlst[0] == 'I' && sqlst[1] == 'M') { + logmsg = "ODBC 3.0"; + } else if (sqlst[0] == 'H' && sqlst[1] == 'Y') { + logmsg = "ODBC 3.0"; + } else if (sqlst[0] == '2' || sqlst[0] == '0' || sqlst[0] == '4') { + logmsg = "ODBC 3.0"; + } + break; + case SQL_DIAG_CONNECTION_NAME: + case SQL_DIAG_SERVER_NAME: + logmsg = d->dsn ? d->dsn : "No DSN"; + break; + case SQL_DIAG_SQLSTATE: + logmsg = sqlst; + break; + case SQL_DIAG_MESSAGE_TEXT: + if (info) { + clrmsg = logmsg; + } + break; + case SQL_DIAG_NUMBER: + naterr = 1; + /* fall through */ + case SQL_DIAG_NATIVE: + len = strlen(logmsg); + if (len == 0) { + ret = SQL_NO_DATA; + goto done; + } + if (info) { + *((SQLINTEGER *) info) = naterr; + } + ret = SQL_SUCCESS; + goto done; + case SQL_DIAG_DYNAMIC_FUNCTION: + logmsg = ""; + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = (s->isselect == 1 || s->isselect == -1) ? s->nrows : 0; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; + case SQL_DIAG_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = s->isselect ? 0 : s->nrows; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; + default: + goto done; + } + if (info && buflen > 0) { + ((char *) info)[0] = '\0'; + } + len = strlen(logmsg); + if (len == 0) { + ret = SQL_NO_DATA; + goto done; + } + if (stringlen) { + *stringlen = len; + } + if (strbuf) { + if (len >= buflen) { + if (info && buflen > 0) { + if (stringlen) { + *stringlen = buflen - 1; + } + strncpy((char *) info, logmsg, buflen); + ((char *) info)[buflen - 1] = '\0'; + } + } else if (info) { + strcpy((char *) info, logmsg); + } + } + if (clrmsg) { + *clrmsg = '\0'; + } + ret = SQL_SUCCESS; +done: + switch (htype) { + case SQL_HANDLE_DBC: + HDBC_UNLOCK((SQLHDBC) handle); + break; + case SQL_HANDLE_STMT: + HSTMT_UNLOCK((SQLHSTMT) handle); + break; + } + return ret; +} + +#ifndef WINTERFACE +/** + * Get error record given handle (HDBC or HSTMT). + * @param htype handle type + * @param handle HDBC or HSTMT + * @param recno diag record number for which info to be retrieved + * @param id diag id for which info to be retrieved + * @param info output buffer for error message + * @param buflen length of output buffer + * @param stringlen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetDiagField(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLSMALLINT id, SQLPOINTER info, + SQLSMALLINT buflen, SQLSMALLINT *stringlen) +{ + return drvgetdiagfield(htype, handle, recno, id, info, buflen, stringlen); +} +#endif + +#ifdef WINTERFACE +/** + * Get error record given handle (HDBC or HSTMT). + * @param htype handle type + * @param handle HDBC or HSTMT + * @param recno diag record number for which info to be retrieved + * @param id diag id for which info to be retrieved + * @param info output buffer for error message + * @param buflen length of output buffer + * @param stringlen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, + SQLSMALLINT id, SQLPOINTER info, + SQLSMALLINT buflen, SQLSMALLINT *stringlen) +{ + SQLSMALLINT len; + SQLRETURN ret; + + ret = drvgetdiagfield(htype, handle, recno, id, info, buflen, &len); + if (ret == SQL_SUCCESS) { + if (info) { + switch (id) { + case SQL_DIAG_CLASS_ORIGIN: + case SQL_DIAG_SUBCLASS_ORIGIN: + case SQL_DIAG_CONNECTION_NAME: + case SQL_DIAG_SERVER_NAME: + case SQL_DIAG_SQLSTATE: + case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: + if (len > 0) { + SQLWCHAR *m = NULL; + + m = uc_from_utf((unsigned char *) info, len); + if (m) { + if (buflen) { + buflen /= sizeof (SQLWCHAR); + uc_strncpy(info, m, buflen); + m[len] = 0; + len = min(buflen, uc_strlen(m)); + } else { + len = uc_strlen(m); + } + uc_free(m); + len *= sizeof (SQLWCHAR); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (buflen > 0) { + ((SQLWCHAR *) info)[0] = 0; + } + } + } + } else { + switch (id) { + case SQL_DIAG_CLASS_ORIGIN: + case SQL_DIAG_SUBCLASS_ORIGIN: + case SQL_DIAG_CONNECTION_NAME: + case SQL_DIAG_SERVER_NAME: + case SQL_DIAG_SQLSTATE: + case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: + len *= sizeof (SQLWCHAR); + break; + } + } + if (stringlen) { + *stringlen = len; + } + } + return ret; +} +#endif + +/** + * Internal get option of HSTMT. + * @param stmt statement handle + * @param attr attribute to be retrieved + * @param val output buffer + * @param bufmax length of output buffer + * @param buflen output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + STMT *s = (STMT *) stmt; + SQLULEN *uval = (SQLULEN *) val; + + switch (attr) { + case SQL_QUERY_TIMEOUT: + *uval = 0; + return SQL_SUCCESS; + case SQL_ATTR_CURSOR_TYPE: + *uval = s->curtype; + return SQL_SUCCESS; + case SQL_ATTR_CURSOR_SCROLLABLE: + *uval = (s->curtype != SQL_CURSOR_FORWARD_ONLY) ? + SQL_SCROLLABLE : SQL_NONSCROLLABLE; + return SQL_SUCCESS; +#ifdef SQL_ATTR_CURSOR_SENSITIVITY + case SQL_ATTR_CURSOR_SENSITIVITY: + *uval = SQL_UNSPECIFIED; + return SQL_SUCCESS; +#endif + case SQL_ATTR_ROW_NUMBER: + if (s->s4stmt) { + *uval = (s->s4stmt_rownum < 0) ? + SQL_ROW_NUMBER_UNKNOWN : (s->s4stmt_rownum + 1); + } else { + *uval = (s->rowp < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->rowp + 1); + } + return SQL_SUCCESS; + case SQL_ATTR_ASYNC_ENABLE: + *uval = SQL_ASYNC_ENABLE_OFF; + return SQL_SUCCESS; + case SQL_CONCURRENCY: + *uval = SQL_CONCUR_LOCK; + return SQL_SUCCESS; + case SQL_ATTR_RETRIEVE_DATA: + *uval = s->retr_data; + return SQL_SUCCESS; + case SQL_ROWSET_SIZE: + case SQL_ATTR_ROW_ARRAY_SIZE: + *uval = s->rowset_size; + return SQL_SUCCESS; + /* Needed for some driver managers, but dummies for now */ + case SQL_ATTR_IMP_ROW_DESC: + case SQL_ATTR_APP_ROW_DESC: + case SQL_ATTR_IMP_PARAM_DESC: + case SQL_ATTR_APP_PARAM_DESC: + *((SQLHDESC *) val) = (SQLHDESC) DEAD_MAGIC; + return SQL_SUCCESS; + case SQL_ATTR_ROW_STATUS_PTR: + *((SQLUSMALLINT **) val) = s->row_status; + return SQL_SUCCESS; + case SQL_ATTR_ROWS_FETCHED_PTR: + *((SQLULEN **) val) = s->row_count; + return SQL_SUCCESS; + case SQL_ATTR_USE_BOOKMARKS: { + STMT *s = (STMT *) stmt; + + *(SQLUINTEGER *) val = s->bkmrk; + return SQL_SUCCESS; + } + case SQL_ATTR_FETCH_BOOKMARK_PTR: + *(SQLPOINTER *) val = s->bkmrkptr; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: + *((SQLULEN **) val) = s->parm_bind_offs; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_BIND_TYPE: + *((SQLULEN *) val) = s->parm_bind_type; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_OPERATION_PTR: + *((SQLUSMALLINT **) val) = s->parm_oper; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_STATUS_PTR: + *((SQLUSMALLINT **) val) = s->parm_status; + return SQL_SUCCESS; + case SQL_ATTR_PARAMS_PROCESSED_PTR: + *((SQLULEN **) val) = s->parm_proc; + return SQL_SUCCESS; + case SQL_ATTR_PARAMSET_SIZE: + *((SQLULEN *) val) = s->paramset_size; + return SQL_SUCCESS; + case SQL_ATTR_ROW_BIND_TYPE: + *(SQLULEN *) val = s->bind_type; + return SQL_SUCCESS; + case SQL_ATTR_ROW_BIND_OFFSET_PTR: + *((SQLULEN **) val) = s->bind_offs; + return SQL_SUCCESS; + case SQL_ATTR_MAX_ROWS: + *((SQLULEN *) val) = s->max_rows; + case SQL_ATTR_MAX_LENGTH: + *((SQLINTEGER *) val) = 1000000000; + return SQL_SUCCESS; +#ifdef SQL_ATTR_METADATA_ID + case SQL_ATTR_METADATA_ID: + *((SQLULEN *) val) = SQL_FALSE; + return SQL_SUCCESS; +#endif + } + return drvunimplstmt(stmt); +} + +#if (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) || !defined(WINTERFACE) +/** + * Get option of HSTMT. + * @param stmt statement handle + * @param attr attribute to be retrieved + * @param val output buffer + * @param bufmax length of output buffer + * @param buflen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgetstmtattr(stmt, attr, val, bufmax, buflen); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Get option of HSTMT (UNICODE version). + * @param stmt statement handle + * @param attr attribute to be retrieved + * @param val output buffer + * @param bufmax length of output buffer + * @param buflen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetStmtAttrW(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgetstmtattr(stmt, attr, val, bufmax, buflen); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal set option on HSTMT. + * @param stmt statement handle + * @param attr attribute to be set + * @param val input buffer (attribute value) + * @param buflen length of input buffer + * @result ODBC error code + */ + +static SQLRETURN +drvsetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER buflen) +{ + STMT *s = (STMT *) stmt; +#if defined(SQL_BIGINT) && defined(__WORDSIZE) && (__WORDSIZE == 64) + SQLBIGINT uval; + + uval = (SQLBIGINT) val; +#else + SQLULEN uval; + + uval = (SQLULEN) val; +#endif + switch (attr) { + case SQL_ATTR_CURSOR_TYPE: + if (val == (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY) { + s->curtype = SQL_CURSOR_FORWARD_ONLY; + } else { + s->curtype = SQL_CURSOR_STATIC; + } + if (val != (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY && + val != (SQLPOINTER) SQL_CURSOR_STATIC) { + goto e01s02; + } + return SQL_SUCCESS; + case SQL_ATTR_CURSOR_SCROLLABLE: + if (val == (SQLPOINTER) SQL_NONSCROLLABLE) { + s->curtype = SQL_CURSOR_FORWARD_ONLY; + } else { + s->curtype = SQL_CURSOR_STATIC; + } + return SQL_SUCCESS; + case SQL_ATTR_ASYNC_ENABLE: + if (val != (SQLPOINTER) SQL_ASYNC_ENABLE_OFF) { + e01s02: + setstat(s, -1, "option value changed", "01S02"); + return SQL_SUCCESS_WITH_INFO; + } + return SQL_SUCCESS; + case SQL_CONCURRENCY: + if (val != (SQLPOINTER) SQL_CONCUR_LOCK) { + goto e01s02; + } + return SQL_SUCCESS; +#ifdef SQL_ATTR_CURSOR_SENSITIVITY + case SQL_ATTR_CURSOR_SENSITIVITY: + if (val != (SQLPOINTER) SQL_UNSPECIFIED) { + goto e01s02; + } + return SQL_SUCCESS; +#endif + case SQL_ATTR_QUERY_TIMEOUT: + return SQL_SUCCESS; + case SQL_ATTR_RETRIEVE_DATA: + if (val != (SQLPOINTER) SQL_RD_ON && + val != (SQLPOINTER) SQL_RD_OFF) { + goto e01s02; + } + s->retr_data = uval; + return SQL_SUCCESS; + case SQL_ROWSET_SIZE: + case SQL_ATTR_ROW_ARRAY_SIZE: + if (uval < 1) { + setstat(s, -1, "invalid rowset size", "HY000"); + return SQL_ERROR; + } else { + SQLUSMALLINT *rst = &s->row_status1; + + if (uval > 1) { + rst = xmalloc(sizeof (SQLUSMALLINT) * uval); + if (!rst) { + return nomem(s); + } + } + if (s->row_status0 != &s->row_status1) { + freep(&s->row_status0); + } + s->row_status0 = rst; + s->rowset_size = uval; + } + return SQL_SUCCESS; + case SQL_ATTR_ROW_STATUS_PTR: + s->row_status = (SQLUSMALLINT *) val; + return SQL_SUCCESS; + case SQL_ATTR_ROWS_FETCHED_PTR: + s->row_count = (SQLULEN *) val; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_BIND_OFFSET_PTR: + s->parm_bind_offs = (SQLULEN *) val; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_BIND_TYPE: + s->parm_bind_type = uval; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_OPERATION_PTR: + s->parm_oper = (SQLUSMALLINT *) val; + return SQL_SUCCESS; + case SQL_ATTR_PARAM_STATUS_PTR: + s->parm_status = (SQLUSMALLINT *) val; + return SQL_SUCCESS; + case SQL_ATTR_PARAMS_PROCESSED_PTR: + s->parm_proc = (SQLULEN *) val; + return SQL_SUCCESS; + case SQL_ATTR_PARAMSET_SIZE: + if (uval < 1) { + goto e01s02; + } + s->paramset_size = uval; + s->paramset_count = 0; + return SQL_SUCCESS; + case SQL_ATTR_ROW_BIND_TYPE: + s->bind_type = uval; + return SQL_SUCCESS; + case SQL_ATTR_ROW_BIND_OFFSET_PTR: + s->bind_offs = (SQLULEN *) val; + return SQL_SUCCESS; + case SQL_ATTR_USE_BOOKMARKS: + if (val != (SQLPOINTER) SQL_UB_OFF && + val != (SQLPOINTER) SQL_UB_ON && + val != (SQLPOINTER) SQL_UB_VARIABLE) { + goto e01s02; + } + if (*s->ov3 && val == (SQLPOINTER) SQL_UB_VARIABLE) { + s->bkmrk = SQL_UB_VARIABLE; + return SQL_SUCCESS; + } + if (val == (SQLPOINTER) SQL_UB_VARIABLE) { + s->bkmrk = SQL_UB_ON; + goto e01s02; + } + s->bkmrk = (val == (SQLPOINTER) SQL_UB_ON) ? SQL_UB_ON : SQL_UB_OFF; + return SQL_SUCCESS; + case SQL_ATTR_FETCH_BOOKMARK_PTR: + s->bkmrkptr = (SQLINTEGER *) val; + return SQL_SUCCESS; + case SQL_ATTR_MAX_ROWS: + s->max_rows = uval; + return SQL_SUCCESS; + case SQL_ATTR_MAX_LENGTH: + if (val != (SQLPOINTER) 1000000000) { + goto e01s02; + } + return SQL_SUCCESS; +#ifdef SQL_ATTR_METADATA_ID + case SQL_ATTR_METADATA_ID: + if (val != (SQLPOINTER) SQL_FALSE) { + goto e01s02; + } + return SQL_SUCCESS; +#endif + } + return drvunimplstmt(stmt); +} + +#if (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) || !defined(WINTERFACE) +/** + * Set option on HSTMT. + * @param stmt statement handle + * @param attr attribute to be set + * @param val input buffer (attribute value) + * @param buflen length of input buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER buflen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvsetstmtattr(stmt, attr, val, buflen); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Set option on HSTMT (UNICODE version). + * @param stmt statement handle + * @param attr attribute to be set + * @param val input buffer (attribute value) + * @param buflen length of input buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetStmtAttrW(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER buflen) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvsetstmtattr(stmt, attr, val, buflen); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal get option of HSTMT. + * @param stmt statement handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +static SQLRETURN +drvgetstmtoption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param) +{ + STMT *s = (STMT *) stmt; + SQLUINTEGER *ret = (SQLUINTEGER *) param; + + switch (opt) { + case SQL_QUERY_TIMEOUT: + *ret = 0; + return SQL_SUCCESS; + case SQL_CURSOR_TYPE: + *ret = s->curtype; + return SQL_SUCCESS; + case SQL_ROW_NUMBER: + if (s->s4stmt) { + *ret = (s->s4stmt_rownum < 0) ? + SQL_ROW_NUMBER_UNKNOWN : (s->s4stmt_rownum + 1); + } else { + *ret = (s->rowp < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->rowp + 1); + } + return SQL_SUCCESS; + case SQL_ASYNC_ENABLE: + *ret = SQL_ASYNC_ENABLE_OFF; + return SQL_SUCCESS; + case SQL_CONCURRENCY: + *ret = SQL_CONCUR_LOCK; + return SQL_SUCCESS; + case SQL_ATTR_RETRIEVE_DATA: + *ret = s->retr_data; + return SQL_SUCCESS; + case SQL_ROWSET_SIZE: + case SQL_ATTR_ROW_ARRAY_SIZE: + *ret = s->rowset_size; + return SQL_SUCCESS; + case SQL_ATTR_MAX_ROWS: + *ret = s->max_rows; + return SQL_SUCCESS; + case SQL_ATTR_MAX_LENGTH: + *ret = 1000000000; + return SQL_SUCCESS; + } + return drvunimplstmt(stmt); +} + +/** + * Get option of HSTMT. + * @param stmt statement handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetStmtOption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgetstmtoption(stmt, opt, param); + HSTMT_UNLOCK(stmt); + return ret; +} + +#ifdef WINTERFACE +/** + * Get option of HSTMT (UNICODE version). + * @param stmt statement handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetStmtOptionW(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgetstmtoption(stmt, opt, param); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal set option on HSTMT. + * @param stmt statement handle + * @param opt option to be set + * @param param input buffer (option value) + * @result ODBC error code + */ + +static SQLRETURN +drvsetstmtoption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLUINTEGER param) +{ + STMT *s = (STMT *) stmt; + + switch (opt) { + case SQL_CURSOR_TYPE: + if (param == SQL_CURSOR_FORWARD_ONLY) { + s->curtype = param; + } else { + s->curtype = SQL_CURSOR_STATIC; + } + if (param != SQL_CURSOR_FORWARD_ONLY && + param != SQL_CURSOR_STATIC) { + goto e01s02; + } + return SQL_SUCCESS; + case SQL_ASYNC_ENABLE: + if (param != SQL_ASYNC_ENABLE_OFF) { + goto e01s02; + } + return SQL_SUCCESS; + case SQL_CONCURRENCY: + if (param != SQL_CONCUR_LOCK) { + goto e01s02; + } + return SQL_SUCCESS; + case SQL_QUERY_TIMEOUT: + return SQL_SUCCESS; + case SQL_RETRIEVE_DATA: + if (param != SQL_RD_ON && param != SQL_RD_OFF) { + e01s02: + setstat(s, -1, "option value changed", "01S02"); + return SQL_SUCCESS_WITH_INFO; + } + s->retr_data = (int) param; + return SQL_SUCCESS; + case SQL_ROWSET_SIZE: + case SQL_ATTR_ROW_ARRAY_SIZE: + if (param < 1) { + setstat(s, -1, "invalid rowset size", "HY000"); + return SQL_ERROR; + } else { + SQLUSMALLINT *rst = &s->row_status1; + + if (param > 1) { + rst = xmalloc(sizeof (SQLUSMALLINT) * param); + if (!rst) { + return nomem(s); + } + } + if (s->row_status0 != &s->row_status1) { + freep(&s->row_status0); + } + s->row_status0 = rst; + s->rowset_size = param; + } + return SQL_SUCCESS; + case SQL_ATTR_MAX_ROWS: + s->max_rows = param; + return SQL_SUCCESS; + case SQL_ATTR_MAX_LENGTH: + if (param != 1000000000) { + goto e01s02; + } + return SQL_SUCCESS; + } + return drvunimplstmt(stmt); +} + +/** + * Set option on HSTMT. + * @param stmt statement handle + * @param opt option to be set + * @param param input buffer (option value) + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetStmtOption(SQLHSTMT stmt, SQLUSMALLINT opt, + SETSTMTOPTION_LAST_ARG_TYPE param) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvsetstmtoption(stmt, opt, (SQLUINTEGER) param); + HSTMT_UNLOCK(stmt); + return ret; +} + +#ifdef WINTERFACE +/** + * Set option on HSTMT (UNICODE version). + * @param stmt statement handle + * @param opt option to be set + * @param param input buffer (option value) + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetStmtOptionW(SQLHSTMT stmt, SQLUSMALLINT opt, + SETSTMTOPTION_LAST_ARG_TYPE param) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvsetstmtoption(stmt, opt, (SQLUINTEGER) param); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Check for unbound result columns. + * @param s statement handle + * @result ODBC error code + */ + +static SQLRETURN +chkunbound(STMT *s) +{ + int i; + + if (!s->bindcols || s->nbindcols < s->ncols) { +unbound: + setstat(s, -1, "unbound columns", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + for (i = 0; i < s->ncols; i++) { + BINDCOL *b = &s->bindcols[i]; + + if (b->type == SQL_UNKNOWN_TYPE || !b->valp) { + goto unbound; + } + } + return SQL_SUCCESS; +} + +/** + * Internal handler to setup parameters for positional updates + * from bound user buffers. + * @param s statement handle + * @param stmt SQLite4 statement pointer + * @param i result set column index + * @param si SQLite4 parameter index + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposbind(STMT *s, sqlite4_stmt *stmt, int i, int si, int rsi) +{ + DBC *d = (DBC *) s->dbc; + SQLPOINTER dp = 0; + SQLLEN *lp = 0; + BINDCOL *b = &s->bindcols[i]; + COL *c = &s->cols[i]; + int frac; + char strbuf[128], *cp; + + if (b->valp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi); + } else { + dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi); + } + if (s->bind_offs) { + dp = (SQLPOINTER) ((char *) dp + *s->bind_offs); + } + } + if (b->lenp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi); + } else { + lp = b->lenp + rsi; + } + if (s->bind_offs) { + lp = (SQLLEN *) ((char *) lp + *s->bind_offs); + } + } + if (!dp || !lp) { + setstat(s, -1, "unbound column in positional update", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (*lp == SQL_NULL_DATA) { + sqlite4_bind_null(stmt, si); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: NULL\n", si); + fflush(d->trace); + } + return SQL_SUCCESS; + } + switch (b->type) { + case SQL_C_UTINYINT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + sqlite4_bind_int(stmt, si, *(SQLCHAR *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, *(SQLCHAR *) dp); + fflush(d->trace); + } + break; +#ifdef SQL_BIT + case SQL_C_BIT: + sqlite4_bind_int(stmt, si, (*(SQLCHAR *) dp) ? 1 : 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + (*(SQLCHAR *) dp) ? 1 : 0); + fflush(d->trace); + } + break; +#endif + case SQL_C_USHORT: + sqlite4_bind_int(stmt, si, *(SQLUSMALLINT *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + *(SQLUSMALLINT *) dp); + fflush(d->trace); + } + break; + case SQL_C_SHORT: + case SQL_C_SSHORT: + sqlite4_bind_int(stmt, si, *(SQLSMALLINT *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %d\n", si, + *(SQLSMALLINT *) dp); + fflush(d->trace); + } + break; + case SQL_C_ULONG: + sqlite4_bind_int(stmt, si, *(SQLUINTEGER *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %ld\n", si, + (long) *(SQLUINTEGER *) dp); + fflush(d->trace); + } + break; + case SQL_C_LONG: + case SQL_C_SLONG: + sqlite4_bind_int(stmt, si, *(SQLINTEGER *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %ld\n", si, + (long) *(SQLINTEGER *) dp); + fflush(d->trace); + } + break; +#ifdef SQL_BIGINT + case SQL_C_UBIGINT: + case SQL_C_SBIGINT: + sqlite4_bind_int64(stmt, si, *(SQLBIGINT *) dp); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter %d: %I64d\n", +#else + "-- parameter %d: %lld\n", +#endif + si, (sqlite4_int64) *(SQLBIGINT *) dp); + fflush(d->trace); + } + break; +#endif + case SQL_C_FLOAT: + sqlite4_bind_double(stmt, si, *(float *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, + *(float *) dp); + fflush(d->trace); + } + break; + case SQL_C_DOUBLE: + sqlite4_bind_double(stmt, si, *(double *) dp); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: %g\n", si, + *(double *) dp); + fflush(d->trace); + } + break; + case SQL_C_BINARY: + sqlite4_bind_blob(stmt, si, (char *) dp, *lp, SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: [BLOB]\n", si); + fflush(d->trace); + } + break; +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: + cp = uc_to_utf((SQLWCHAR *) dp, *lp); + if (!cp) { + return nomem(s); + } + sqlite4_bind_text(stmt, si, cp, -1, SQLITE4_TRANSIENT, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, cp); + fflush(d->trace); + } + uc_free(cp); + break; +#endif + case SQL_C_CHAR: +#if defined(_WIN32) || defined(_WIN64) + if (*s->oemcp) { + cp = wmb_to_utf((char *) dp, *lp); + if (!cp) { + return nomem(s); + } + sqlite4_bind_text(stmt, si, cp, -1, SQLITE4_TRANSIENT, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, cp); + fflush(d->trace); + } + uc_free(cp); + } else +#endif + { + if (*lp == SQL_NTS) { + sqlite4_bind_text(stmt, si, (char *) dp, -1, + SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, + (char *) dp); + fflush(d->trace); + } + } else { + sqlite4_bind_text(stmt, si, (char *) dp, *lp, + SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%*s'\n", si, + (int) *lp, (char *) dp); + fflush(d->trace); + } + } + } + break; +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + sprintf(strbuf, "%04d-%02d-%02d", + ((DATE_STRUCT *) dp)->year, + ((DATE_STRUCT *) dp)->month, + ((DATE_STRUCT *) dp)->day); + sqlite4_bind_text(stmt, si, strbuf, -1, SQLITE4_TRANSIENT, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + break; +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + sprintf(strbuf, "%02d:%02d:%02d", + ((TIME_STRUCT *) dp)->hour, + ((TIME_STRUCT *) dp)->minute, + ((TIME_STRUCT *) dp)->second); + sqlite4_bind_text(stmt, si, strbuf, -1, SQLITE4_TRANSIENT, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + frac = (int) ((TIMESTAMP_STRUCT *) dp)->fraction; + frac /= 1000000; + frac = frac % 1000; + if (frac < 0) { + frac = 0; + } + if (c->prec && c->prec <= 16) { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:00.000", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute); + } else if (c->prec && c->prec <= 19) { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:%02d.000", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute, + ((TIMESTAMP_STRUCT *) dp)->second); + } else { + sprintf(strbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", + ((TIMESTAMP_STRUCT *) dp)->year, + ((TIMESTAMP_STRUCT *) dp)->month, + ((TIMESTAMP_STRUCT *) dp)->day, + ((TIMESTAMP_STRUCT *) dp)->hour, + ((TIMESTAMP_STRUCT *) dp)->minute, + ((TIMESTAMP_STRUCT *) dp)->second, + frac); + } + sqlite4_bind_text(stmt, si, strbuf, -1, SQLITE4_TRANSIENT, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, strbuf); + fflush(d->trace); + } + break; + default: + setstat(s, -1, "unsupported column type in positional update", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +/** + * Internal handler to setup parameters for positional updates + * from driver side result set. + * @param s statement handle + * @param stmt SQLite4 statement pointer + * @param i result set column index + * @param si SQLite4 parameter index + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposibind(STMT *s, sqlite4_stmt *stmt, int i, int si, int rsi) +{ + DBC *d = (DBC *) s->dbc; + char **data; + int pos; + + pos = s->rowprs; + if (pos < 0) { + setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107"); + return SQL_ERROR; + } + pos += rsi; + data = s->rows + s->ncols + (pos * s->ncols) + i; + if (*data == NULL) { + sqlite4_bind_null(stmt, si); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: NULL\n", si); + fflush(d->trace); + } + } else { + sqlite4_bind_text(stmt, si, *data, -1, SQLITE4_STATIC, 0); + if (d->trace) { + fprintf(d->trace, "-- parameter %d: '%s'\n", si, *data); + fflush(d->trace); + } + } + return SQL_SUCCESS; +} + +/** + * Internal handler to refresh user buffers from driver side result set. + * @param s statement handle + * @param rsi result set row index + * @result ODBC error code + */ + +static SQLRETURN +setposrefr(STMT *s, int rsi) +{ + int i, withinfo = 0; + SQLRETURN ret = SQL_SUCCESS; + + for (i = 0; s->bindcols && i < s->ncols; i++) { + BINDCOL *b = &s->bindcols[i]; + SQLPOINTER dp = 0; + SQLLEN *lp = 0; + + b->offs = 0; + if (b->valp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi); + } else { + dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi); + } + if (s->bind_offs) { + dp = (SQLPOINTER) ((char *) dp + *s->bind_offs); + } + } + if (b->lenp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi); + } else { + lp = b->lenp + rsi; + } + if (s->bind_offs) { + lp = (SQLLEN *) ((char *) lp + *s->bind_offs); + } + } + if (dp || lp) { + int rowp = s->rowp; + + s->rowp = s->rowprs + rsi; + ret = getrowdata(s, (SQLUSMALLINT) i, b->type, dp, + b->max, lp, 0); + s->rowp = rowp; + if (!SQL_SUCCEEDED(ret)) { + s->row_status0[rsi] = SQL_ROW_ERROR; + break; + } + if (ret != SQL_SUCCESS) { + withinfo = 1; +#ifdef SQL_ROW_SUCCESS_WITH_INFO + s->row_status0[rsi] = SQL_ROW_SUCCESS_WITH_INFO; +#endif + } + } + } + if (SQL_SUCCEEDED(ret)) { + ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS; + } + return ret; +} + +/** + * Internal set position on result in HSTMT. + * @param stmt statement handle + * @param row row to be positioned + * @param op operation code + * @param lock locking type + * @result ODBC error code + */ + +static SQLRETURN +drvsetpos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock) +{ + STMT *s = (STMT *) stmt; + DBC *d = (DBC *) s->dbc; + int rowp, i, k, rc; + dstr *sql = 0; + int sqlleft; + sqlite4_stmt *s4stmt = NULL; + SQLRETURN ret; + + if (lock != SQL_LOCK_NO_CHANGE) { + setstat(s, -1, "unsupported locking mode", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (s->isselect != 1 || s->curtype != SQL_CURSOR_STATIC) { + setstat(s, -1, "incompatible statement", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (op == SQL_ADD) { + if (s->one_tbl <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (row == 0 || row > s->rowset_size + 1) { + goto rowoor; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "INSERT INTO "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? "," : "("); + sql = dsappendq(sql, s->dyncols[i].column); + } + sql = dsappend(sql, ") VALUES "); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ",?" : "(?"); + } + sql = dsappend(sql, ")"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { +istmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s4stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + goto istmterr; + } + sqlite4_finalize(s4stmt); + if (sqlite4_changes(d->sqlite) > 0 && row <= s->rowset_size) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_ADDED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_ADDED; + } + } + return SQL_SUCCESS; + } else if (op == SQL_UPDATE || op == SQL_DELETE) { + if (s->one_tbl <= 0 || s->has_pk <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (row == 0) { + ret = SQL_SUCCESS; + for (i = 1; i <= s->rowset_size; i++) { + ret = drvsetpos(stmt, i, op, lock); + if (!SQL_SUCCEEDED(ret)) { + break; + } + } + return ret; + } + if (row > s->rowset_size) { + goto rowoor; + } + } + if (op != SQL_POSITION && op != SQL_REFRESH && + op != SQL_DELETE && op != SQL_UPDATE) { + return drvunimplstmt(stmt); + } + if (op == SQL_POSITION) { + rowp = s->rowp + row - 1; + if (!s->rows || row == 0 || rowp < -1 || rowp >= s->nrows) { +rowoor: + setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107"); + return SQL_ERROR; + } + s->rowp = rowp; + } else if (op == SQL_REFRESH) { + if (row > s->rowset_size) { + goto rowoor; + } + if (row == 0) { + ret = SQL_SUCCESS; + for (i = 0; i < s->rowset_size; i++) { + ret = setposrefr(s, i); + if (!SQL_SUCCEEDED(ret)) { + break; + } + } + return ret; + } + return setposrefr(s, row - 1); + } else if (op == SQL_DELETE) { + sql = dsappend(sql, "DELETE FROM "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = k = 0; i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + sql = dsappend(sql, (k > 0) ? " AND " : " WHERE "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { +dstmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + ret = setposibind(s, s4stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + goto dstmterr; + } + sqlite4_finalize(s4stmt); + if (sqlite4_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_DELETED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_DELETED; + } + } + return SQL_SUCCESS; + } else if (op == SQL_UPDATE) { + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "UPDATE "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ", " : " SET "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + } + for (i = k = 0; i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + sql = dsappend(sql, (k > 0) ? " AND " : " WHERE "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { +ustmterr: + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s4stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + for (i = 0; s->bindcols && i < s->ncols; i++) { + if (s->dyncols[i].ispk <= 0) { + continue; + } + ret = setposibind(s, s4stmt, i, k, row - 1); + if (ret != SQL_SUCCESS) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + goto ustmterr; + } + sqlite4_finalize(s4stmt); + if (sqlite4_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row - 1] = SQL_ROW_UPDATED; + } + if (s->row_status) { + s->row_status[row - 1] = SQL_ROW_UPDATED; + } + } + return SQL_SUCCESS; + } + return SQL_SUCCESS; +} + +/** + * Set position on result in HSTMT. + * @param stmt statement handle + * @param row row to be positioned + * @param op operation code + * @param lock locking type + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetPos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvsetpos(stmt, row, op, lock); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Internal perform bulk operation on HSTMT. + * @param stmt statement handle + * @param op operation to be performed + * @result ODBC error code + */ + +static SQLRETURN +drvbulkoperations(SQLHSTMT stmt, SQLSMALLINT op) +{ + STMT *s = (STMT *) stmt; + DBC *d = (DBC *) s->dbc; + int row, i, k, rc; + dstr *sql = 0; + int sqlleft; + sqlite4_stmt *s4stmt = NULL; + SQLRETURN ret; + + if (s->isselect != 1 || s->curtype != SQL_CURSOR_STATIC) { + setstat(s, -1, "incompatible statement", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (op == SQL_ADD) { + if (s->one_tbl <= 0) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "INSERT INTO "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? "," : "("); + sql = dsappendq(sql, s->dyncols[i].column); + } + sql = dsappend(sql, ") VALUES "); + for (i = 0; i < s->ncols; i++) { + sql = dsappend(sql, (i > 0) ? ",?" : "(?"); + } + sql = dsappend(sql, ")"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + ret = setposbind(s, s4stmt, i, k, row); + if (ret != SQL_SUCCESS) { +istmterr: + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + ret = SQL_ERROR; + goto istmterr; + } + if (sqlite4_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ADDED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ADDED; + } + } + if (s->bkmrk == SQL_UB_VARIABLE && + s->bkmrkcol.type == SQL_C_VARBOOKMARK && + s->bkmrkcol.valp) { + SQLPOINTER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } +#if 0 + *(sqlite4_int64 *) val = sqlite4_last_insert_rowid(d->sqlite); +#else + *(sqlite4_int64 *) val = 0; +#endif + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + *ival = sizeof (sqlite4_int64); + } + } + dbtraceapi(d, "sqlite4_reset", NULL); + sqlite4_reset(s4stmt); + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return SQL_SUCCESS; + } else if (op == SQL_DELETE_BY_BOOKMARK) { + if (s->has_rowid < 0 || + s->bkmrk != SQL_UB_VARIABLE || + s->bkmrkcol.type != SQL_C_VARBOOKMARK || + !s->bkmrkcol.valp) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + sql = dsappend(sql, "DELETE FROM "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + sql = dsappend(sql, " WHERE "); + sql = dsappendq(sql, s->dyncols[0].column); + sql = dsappend(sql, " = ?"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + SQLPOINTER *val; + sqlite4_int64 rowid; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + if (*ival != sizeof (sqlite4_int64)) { + continue; + } + } + rowid = *(sqlite4_int64 *) val; + sqlite4_bind_int64(s4stmt, 1, rowid); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter 1: %I64d\n", +#else + "-- parameter 1: %lld\n", +#endif + rowid); + fflush(d->trace); + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return SQL_ERROR; + } + if (sqlite4_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_DELETED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_DELETED; + } + } + dbtraceapi(d, "sqlite4_reset", NULL); + sqlite4_reset(s4stmt); + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return SQL_SUCCESS; + } else if (op == SQL_UPDATE_BY_BOOKMARK) { + if (s->has_rowid < 0 || + s->bkmrk != SQL_UB_VARIABLE || + s->bkmrkcol.type != SQL_C_VARBOOKMARK || + !s->bkmrkcol.valp) { + setstat(s, -1, "incompatible rowset", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + ret = chkunbound(s); + if (ret != SQL_SUCCESS) { + return ret; + } + sql = dsappend(sql, "UPDATE "); + if (s->dyncols[0].db && s->dyncols[0].db[0]) { + sql = dsappendq(sql, s->dyncols[0].db); + sql = dsappend(sql, "."); + } + sql = dsappendq(sql, s->dyncols[0].table); + for (i = 0, k = 0; i < s->ncols; i++) { + if (i == s->has_rowid) { + continue; + } + sql = dsappend(sql, (k > 0) ? ", " : " SET "); + sql = dsappendq(sql, s->dyncols[i].column); + sql = dsappend(sql, " = ?"); + k++; + } + sql = dsappend(sql, " WHERE "); + sql = dsappendq(sql, s->dyncols[s->has_rowid].column); + sql = dsappend(sql, " = ?"); + if (dserr(sql)) { + dsfree(sql); + return nomem(s); + } + dbtraceapi(d, "sqlite4_prepare", dsval(sql)); + s4stmt = NULL; + rc = sqlite4_prepare(d->sqlite, dsval(sql), -1, &s4stmt, &sqlleft); + dbtracerc(d, rc, NULL); + dsfree(sql); + if (rc != SQLITE4_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + } + return SQL_ERROR; + } + for (row = 0; row < s->rowset_size; row++) { + SQLPOINTER *val; + sqlite4_int64 rowid; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * row); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * row); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * row); + } else { + ival = &s->bkmrkcol.lenp[row]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + if (*ival != sizeof (sqlite4_int64)) { + continue; + } + } + for (i = 0, k = 1; s->bindcols && i < s->ncols; i++) { + if (i == s->has_rowid) { + continue; + } + ret = setposbind(s, s4stmt, i, k, row); + if (ret != SQL_SUCCESS) { +ustmterr: + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_ERROR; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_ERROR; + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return ret; + } + k++; + } + rowid = *(sqlite4_int64 *) val; + sqlite4_bind_int64(s4stmt, k, rowid); + if (d->trace) { + fprintf(d->trace, +#ifdef _WIN32 + "-- parameter %d: %I64d\n", +#else + "-- parameter %d: %lld\n", +#endif + k, rowid); + fflush(d->trace); + } + rc = sqlite4_step(s4stmt); + if (rc != SQLITE4_DONE) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), rc); + ret = SQL_ERROR; + goto ustmterr; + } + if (sqlite4_changes(d->sqlite) > 0) { + if (s->row_status0) { + s->row_status0[row] = SQL_ROW_UPDATED; + } + if (s->row_status) { + s->row_status[row] = SQL_ROW_UPDATED; + } + } + dbtraceapi(d, "sqlite4_reset", NULL); + sqlite4_reset(s4stmt); + } + dbtraceapi(d, "sqlite4_finalize", NULL); + sqlite4_finalize(s4stmt); + return SQL_SUCCESS; + } + setstat(s, -1, "unsupported operation", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; +} + +/** + * Perform bulk operation on HSTMT. + * @param stmt statement handle + * @param oper operation to be performed + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLBulkOperations(SQLHSTMT stmt, SQLSMALLINT oper) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbulkoperations(stmt, oper); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Function not implemented. + */ + +SQLRETURN SQL_API +SQLSetScrollOptions(SQLHSTMT stmt, SQLUSMALLINT concur, SQLLEN rowkeyset, + SQLUSMALLINT rowset) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvunimplstmt(stmt); + HSTMT_UNLOCK(stmt); + return ret; +} + +#define strmak(dst, src, max, lenp) { \ + int len = strlen(src); \ + int cnt = min(len + 1, max); \ + strncpy(dst, src, cnt); \ + *lenp = (cnt > len) ? len : cnt; \ +} + +/** + * Internal return information about what this ODBC driver supports. + * @param dbc database connection handle + * @param type type of information to be retrieved + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, + SQLSMALLINT *valLen) +{ + DBC *d; + char dummyc[16]; + SQLSMALLINT dummy; +#if defined(_WIN32) || defined(_WIN64) + char pathbuf[301], *drvname; +#else + static char drvname[] = "sqlite4odbc.so"; +#endif + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (valMax) { + valMax--; + } + if (!valLen) { + valLen = &dummy; + } + if (!val) { + val = dummyc; + valMax = sizeof (dummyc) - 1; + } + switch (type) { + case SQL_MAX_USER_NAME_LEN: + *((SQLSMALLINT *) val) = 16; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_USER_NAME: + strmak(val, "", valMax, valLen); + break; + case SQL_DRIVER_ODBC_VER: +#if 0 + strmak(val, (*d->ov3) ? "03.00" : "02.50", valMax, valLen); +#else + strmak(val, "03.00", valMax, valLen); +#endif + break; + case SQL_ACTIVE_CONNECTIONS: + case SQL_ACTIVE_STATEMENTS: + *((SQLSMALLINT *) val) = 0; + *valLen = sizeof (SQLSMALLINT); + break; +#ifdef SQL_ASYNC_MODE + case SQL_ASYNC_MODE: + *((SQLUINTEGER *) val) = SQL_AM_NONE; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_CREATE_TABLE + case SQL_CREATE_TABLE: + *((SQLUINTEGER *) val) = SQL_CT_CREATE_TABLE | + SQL_CT_COLUMN_DEFAULT | + SQL_CT_COLUMN_CONSTRAINT | + SQL_CT_CONSTRAINT_NON_DEFERRABLE; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_CREATE_VIEW + case SQL_CREATE_VIEW: + *((SQLUINTEGER *) val) = SQL_CV_CREATE_VIEW; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_DDL_INDEX + case SQL_DDL_INDEX: + *((SQLUINTEGER *) val) = SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_DROP_TABLE + case SQL_DROP_TABLE: + *((SQLUINTEGER *) val) = SQL_DT_DROP_TABLE; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_DROP_VIEW + case SQL_DROP_VIEW: + *((SQLUINTEGER *) val) = SQL_DV_DROP_VIEW; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_INDEX_KEYWORDS + case SQL_INDEX_KEYWORDS: + *((SQLUINTEGER *) val) = SQL_IK_ALL; + *valLen = sizeof (SQLUINTEGER); + break; +#endif + case SQL_DATA_SOURCE_NAME: + strmak(val, d->dsn ? d->dsn : "", valMax, valLen); + break; + case SQL_DRIVER_NAME: +#if defined(_WIN32) || defined(_WIN64) + GetModuleFileName(hModule, pathbuf, sizeof (pathbuf)); + drvname = strrchr(pathbuf, '\\'); + if (drvname == NULL) { + drvname = strrchr(pathbuf, '/'); + } + if (drvname == NULL) { + drvname = pathbuf; + } else { + drvname++; + } +#endif + strmak(val, drvname, valMax, valLen); + break; + case SQL_DRIVER_VER: + strmak(val, DRIVER_VER_INFO, valMax, valLen); + break; + case SQL_FETCH_DIRECTION: + *((SQLUINTEGER *) val) = SQL_FD_FETCH_NEXT | SQL_FD_FETCH_FIRST | + SQL_FD_FETCH_LAST | SQL_FD_FETCH_PRIOR | SQL_FD_FETCH_ABSOLUTE; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_ODBC_VER: + strmak(val, (*d->ov3) ? "03.00" : "02.50", valMax, valLen); + break; + case SQL_ODBC_SAG_CLI_CONFORMANCE: + *((SQLSMALLINT *) val) = SQL_OSCC_NOT_COMPLIANT; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_STANDARD_CLI_CONFORMANCE: + *((SQLUINTEGER *) val) = SQL_SCC_XOPEN_CLI_VERSION1; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_SERVER_NAME: + case SQL_DATABASE_NAME: + strmak(val, d->dbname ? d->dbname : "", valMax, valLen); + break; + case SQL_SEARCH_PATTERN_ESCAPE: + strmak(val, "\\", valMax, valLen); + break; + case SQL_ODBC_SQL_CONFORMANCE: + *((SQLSMALLINT *) val) = SQL_OSC_MINIMUM; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_ODBC_API_CONFORMANCE: + *((SQLSMALLINT *) val) = SQL_OAC_LEVEL1; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_DBMS_NAME: + strmak(val, "SQLite", valMax, valLen); + break; + case SQL_DBMS_VER: + strmak(val, SQLITE4_VERSION, valMax, valLen); + break; + case SQL_COLUMN_ALIAS: + case SQL_NEED_LONG_DATA_LEN: + strmak(val, "Y", valMax, valLen); + break; + case SQL_ROW_UPDATES: + case SQL_ACCESSIBLE_PROCEDURES: + case SQL_PROCEDURES: + case SQL_EXPRESSIONS_IN_ORDERBY: + case SQL_ODBC_SQL_OPT_IEF: + case SQL_LIKE_ESCAPE_CLAUSE: + case SQL_ORDER_BY_COLUMNS_IN_SELECT: + case SQL_OUTER_JOINS: + case SQL_ACCESSIBLE_TABLES: + case SQL_MULT_RESULT_SETS: + case SQL_MULTIPLE_ACTIVE_TXN: + case SQL_MAX_ROW_SIZE_INCLUDES_LONG: + strmak(val, "N", valMax, valLen); + break; +#ifdef SQL_CATALOG_NAME + case SQL_CATALOG_NAME: +#if defined(_WIN32) || defined(_WIN64) + strmak(val, d->xcelqrx ? "Y" : "N", valMax, valLen); +#else + strmak(val, "N", valMax, valLen); +#endif + break; +#endif + case SQL_DATA_SOURCE_READ_ONLY: + strmak(val, "N", valMax, valLen); + break; +#ifdef SQL_OJ_CAPABILITIES + case SQL_OJ_CAPABILITIES: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; +#endif +#ifdef SQL_MAX_IDENTIFIER_LEN + case SQL_MAX_IDENTIFIER_LEN: + *((SQLUSMALLINT *) val) = 255; + *valLen = sizeof (SQLUSMALLINT); + break; +#endif + case SQL_CONCAT_NULL_BEHAVIOR: + *((SQLSMALLINT *) val) = SQL_CB_NULL; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_CURSOR_COMMIT_BEHAVIOR: + case SQL_CURSOR_ROLLBACK_BEHAVIOR: + *((SQLSMALLINT *) val) = SQL_CB_PRESERVE; + *valLen = sizeof (SQLSMALLINT); + break; +#ifdef SQL_CURSOR_SENSITIVITY + case SQL_CURSOR_SENSITIVITY: + *((SQLUINTEGER *) val) = SQL_UNSPECIFIED; + *valLen = sizeof (SQLUINTEGER); + break; +#endif + case SQL_DEFAULT_TXN_ISOLATION: + *((SQLUINTEGER *) val) = SQL_TXN_SERIALIZABLE; + *valLen = sizeof (SQLUINTEGER); + break; +#ifdef SQL_DESCRIBE_PARAMETER + case SQL_DESCRIBE_PARAMETER: + strmak(val, "Y", valMax, valLen); + break; +#endif + case SQL_TXN_ISOLATION_OPTION: + *((SQLUINTEGER *) val) = SQL_TXN_SERIALIZABLE; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_IDENTIFIER_CASE: + *((SQLSMALLINT *) val) = SQL_IC_SENSITIVE; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_IDENTIFIER_QUOTE_CHAR: + strmak(val, "\"", valMax, valLen); + break; + case SQL_MAX_TABLE_NAME_LEN: + case SQL_MAX_COLUMN_NAME_LEN: + *((SQLSMALLINT *) val) = 255; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_MAX_CURSOR_NAME_LEN: + *((SQLSMALLINT *) val) = 255; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_MAX_PROCEDURE_NAME_LEN: + *((SQLSMALLINT *) val) = 0; + break; + case SQL_MAX_QUALIFIER_NAME_LEN: + case SQL_MAX_OWNER_NAME_LEN: + *((SQLSMALLINT *) val) = 255; + break; + case SQL_OWNER_TERM: + strmak(val, "", valMax, valLen); + break; + case SQL_PROCEDURE_TERM: + strmak(val, "PROCEDURE", valMax, valLen); + break; + case SQL_QUALIFIER_NAME_SEPARATOR: + strmak(val, ".", valMax, valLen); + break; + case SQL_QUALIFIER_TERM: +#if defined(_WIN32) || defined(_WIN64) + strmak(val, d->xcelqrx ? "catalog" : "", valMax, valLen); +#else + strmak(val, "", valMax, valLen); +#endif + break; + case SQL_QUALIFIER_USAGE: +#if defined(_WIN32) || defined(_WIN64) + *((SQLUINTEGER *) val) = d->xcelqrx ? + (SQL_CU_DML_STATEMENTS | SQL_CU_INDEX_DEFINITION | + SQL_CU_TABLE_DEFINITION) : 0; +#else + *((SQLUINTEGER *) val) = 0; +#endif + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_SCROLL_CONCURRENCY: + *((SQLUINTEGER *) val) = SQL_SCCO_LOCK; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_SCROLL_OPTIONS: + *((SQLUINTEGER *) val) = SQL_SO_STATIC | SQL_SO_FORWARD_ONLY; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_TABLE_TERM: + strmak(val, "TABLE", valMax, valLen); + break; + case SQL_TXN_CAPABLE: + *((SQLSMALLINT *) val) = SQL_TC_ALL; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_CONVERT_FUNCTIONS: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_SYSTEM_FUNCTIONS: + case SQL_NUMERIC_FUNCTIONS: + case SQL_STRING_FUNCTIONS: + case SQL_TIMEDATE_FUNCTIONS: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_CONVERT_BIGINT: + case SQL_CONVERT_BIT: + case SQL_CONVERT_CHAR: + case SQL_CONVERT_DATE: + case SQL_CONVERT_DECIMAL: + case SQL_CONVERT_DOUBLE: + case SQL_CONVERT_FLOAT: + case SQL_CONVERT_INTEGER: + case SQL_CONVERT_LONGVARCHAR: + case SQL_CONVERT_NUMERIC: + case SQL_CONVERT_REAL: + case SQL_CONVERT_SMALLINT: + case SQL_CONVERT_TIME: + case SQL_CONVERT_TIMESTAMP: + case SQL_CONVERT_TINYINT: + case SQL_CONVERT_VARCHAR: + *((SQLUINTEGER *) val) = + SQL_CVT_CHAR | SQL_CVT_NUMERIC | SQL_CVT_DECIMAL | + SQL_CVT_INTEGER | SQL_CVT_SMALLINT | SQL_CVT_FLOAT | SQL_CVT_REAL | + SQL_CVT_DOUBLE | SQL_CVT_VARCHAR | SQL_CVT_LONGVARCHAR | + SQL_CVT_BIT | SQL_CVT_TINYINT | SQL_CVT_BIGINT | + SQL_CVT_DATE | SQL_CVT_TIME | SQL_CVT_TIMESTAMP; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_CONVERT_BINARY: + case SQL_CONVERT_VARBINARY: + case SQL_CONVERT_LONGVARBINARY: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_POSITIONED_STATEMENTS: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_LOCK_TYPES: + *((SQLUINTEGER *) val) = SQL_LCK_NO_CHANGE; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_BOOKMARK_PERSISTENCE: + *((SQLUINTEGER *) val) = SQL_BP_SCROLL; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_UNION: + *((SQLUINTEGER *) val) = SQL_U_UNION | SQL_U_UNION_ALL; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_OWNER_USAGE: + case SQL_SUBQUERIES: + case SQL_TIMEDATE_ADD_INTERVALS: + case SQL_TIMEDATE_DIFF_INTERVALS: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_QUOTED_IDENTIFIER_CASE: + *((SQLUSMALLINT *) val) = SQL_IC_SENSITIVE; + *valLen = sizeof (SQLUSMALLINT); + break; + case SQL_POS_OPERATIONS: + *((SQLUINTEGER *) val) = SQL_POS_POSITION | SQL_POS_UPDATE | + SQL_POS_DELETE | SQL_POS_ADD | SQL_POS_REFRESH; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_ALTER_TABLE: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_CORRELATION_NAME: + *((SQLSMALLINT *) val) = SQL_CN_DIFFERENT; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_NON_NULLABLE_COLUMNS: + *((SQLSMALLINT *) val) = SQL_NNC_NON_NULL; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_NULL_COLLATION: + *((SQLSMALLINT *) val) = SQL_NC_START; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_MAX_COLUMNS_IN_GROUP_BY: + case SQL_MAX_COLUMNS_IN_ORDER_BY: + case SQL_MAX_COLUMNS_IN_SELECT: + case SQL_MAX_COLUMNS_IN_TABLE: + case SQL_MAX_ROW_SIZE: + case SQL_MAX_TABLES_IN_SELECT: + *((SQLSMALLINT *) val) = 0; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_MAX_BINARY_LITERAL_LEN: + case SQL_MAX_CHAR_LITERAL_LEN: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_MAX_COLUMNS_IN_INDEX: + *((SQLSMALLINT *) val) = 0; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_MAX_INDEX_SIZE: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; +#ifdef SQL_MAX_IDENTIFIER_LENGTH + case SQL_MAX_IDENTIFIER_LENGTH: + *((SQLUINTEGER *) val) = 255; + *valLen = sizeof (SQLUINTEGER); + break; +#endif + case SQL_MAX_STATEMENT_LEN: + *((SQLUINTEGER *) val) = 16384; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_QUALIFIER_LOCATION: + *((SQLSMALLINT *) val) = SQL_QL_START; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_GETDATA_EXTENSIONS: + *((SQLUINTEGER *) val) = + SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_STATIC_SENSITIVITY: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_FILE_USAGE: +#if defined(_WIN32) || defined(_WIN64) + *((SQLSMALLINT *) val) = + d->xcelqrx ? SQL_FILE_CATALOG : SQL_FILE_NOT_SUPPORTED; +#else + *((SQLSMALLINT *) val) = SQL_FILE_NOT_SUPPORTED; +#endif + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_GROUP_BY: + *((SQLSMALLINT *) val) = 0; + *valLen = sizeof (SQLSMALLINT); + break; + case SQL_KEYWORDS: + strmak(val, "CREATE,SELECT,DROP,DELETE,UPDATE,INSERT," + "INTO,VALUES,TABLE,INDEX,FROM,SET,WHERE,AND,CURRENT,OF", + valMax, valLen); + break; + case SQL_SPECIAL_CHARACTERS: +#ifdef SQL_COLLATION_SEQ + case SQL_COLLATION_SEQ: +#endif + strmak(val, "", valMax, valLen); + break; + case SQL_BATCH_SUPPORT: + case SQL_BATCH_ROW_COUNT: + case SQL_PARAM_ARRAY_ROW_COUNTS: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: + *((SQLUINTEGER *) val) = SQL_CA1_NEXT | SQL_CA1_BOOKMARK; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_STATIC_CURSOR_ATTRIBUTES1: + *((SQLUINTEGER *) val) = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | + SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK | SQL_CA1_POS_POSITION | + SQL_CA1_POS_DELETE | SQL_CA1_POS_UPDATE | SQL_CA1_POS_REFRESH | + SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_BULK_ADD | + SQL_CA1_BULK_UPDATE_BY_BOOKMARK | SQL_CA1_BULK_DELETE_BY_BOOKMARK; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: + case SQL_STATIC_CURSOR_ATTRIBUTES2: + *((SQLUINTEGER *) val) = SQL_CA2_READ_ONLY_CONCURRENCY | + SQL_CA2_LOCK_CONCURRENCY; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_KEYSET_CURSOR_ATTRIBUTES1: + case SQL_KEYSET_CURSOR_ATTRIBUTES2: + case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: + case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: + *((SQLUINTEGER *) val) = 0; + *valLen = sizeof (SQLUINTEGER); + break; + case SQL_ODBC_INTERFACE_CONFORMANCE: + *((SQLUINTEGER *) val) = SQL_OIC_CORE; + *valLen = sizeof (SQLUINTEGER); + break; + default: + setstatd(d, -1, "unsupported info option %d", + (*d->ov3) ? "HYC00" : "S1C00", type); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +#if (defined(HAVE_UNIXODBC) && (HAVE_UNIXODBC)) || !defined(WINTERFACE) +/** + * Return information about what this ODBC driver supports. + * @param dbc database connection handle + * @param type type of information to be retrieved + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetInfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, + SQLSMALLINT *valLen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvgetinfo(dbc, type, val, valMax, valLen); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Return information about what this ODBC driver supports. + * @param dbc database connection handle + * @param type type of information to be retrieved + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetInfoW(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, + SQLSMALLINT *valLen) +{ + SQLRETURN ret; + SQLSMALLINT len = 0; + + HDBC_LOCK(dbc); + ret = drvgetinfo(dbc, type, val, valMax, &len); + HDBC_UNLOCK(dbc); + if (ret == SQL_SUCCESS) { + SQLWCHAR *v = NULL; + + switch (type) { + case SQL_USER_NAME: + case SQL_DRIVER_ODBC_VER: + case SQL_DATA_SOURCE_NAME: + case SQL_DRIVER_NAME: + case SQL_DRIVER_VER: + case SQL_ODBC_VER: + case SQL_SERVER_NAME: + case SQL_DATABASE_NAME: + case SQL_SEARCH_PATTERN_ESCAPE: + case SQL_DBMS_NAME: + case SQL_DBMS_VER: + case SQL_NEED_LONG_DATA_LEN: + case SQL_ROW_UPDATES: + case SQL_ACCESSIBLE_PROCEDURES: + case SQL_PROCEDURES: + case SQL_EXPRESSIONS_IN_ORDERBY: + case SQL_ODBC_SQL_OPT_IEF: + case SQL_LIKE_ESCAPE_CLAUSE: + case SQL_ORDER_BY_COLUMNS_IN_SELECT: + case SQL_OUTER_JOINS: + case SQL_COLUMN_ALIAS: + case SQL_ACCESSIBLE_TABLES: + case SQL_MULT_RESULT_SETS: + case SQL_MULTIPLE_ACTIVE_TXN: + case SQL_MAX_ROW_SIZE_INCLUDES_LONG: + case SQL_DATA_SOURCE_READ_ONLY: +#ifdef SQL_DESCRIBE_PARAMETER + case SQL_DESCRIBE_PARAMETER: +#endif + case SQL_IDENTIFIER_QUOTE_CHAR: + case SQL_OWNER_TERM: + case SQL_PROCEDURE_TERM: + case SQL_QUALIFIER_NAME_SEPARATOR: + case SQL_QUALIFIER_TERM: + case SQL_TABLE_TERM: + case SQL_KEYWORDS: + case SQL_SPECIAL_CHARACTERS: +#ifdef SQL_CATALOG_NAME + case SQL_CATALOG_NAME: +#endif +#ifdef SQL_COLLATION_SEQ + case SQL_COLLATION_SEQ: +#endif + if (val) { + if (len > 0) { + v = uc_from_utf((SQLCHAR *) val, len); + if (v) { + int vmax = valMax / sizeof (SQLWCHAR); + + uc_strncpy(val, v, vmax); + v[len] = 0; + len = min(vmax, uc_strlen(v)); + uc_free(v); + len *= sizeof (SQLWCHAR); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (valMax >= sizeof (SQLWCHAR)) { + *((SQLWCHAR *)val) = 0; + } + } + } else { + len *= sizeof (SQLWCHAR); + } + break; + } + if (valLen) { + *valLen = len; + } + } + return ret; +} +#endif + +/** + * Return information about supported ODBC API functions. + * @param dbc database connection handle + * @param func function code to be retrieved + * @param flags output indicator + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetFunctions(SQLHDBC dbc, SQLUSMALLINT func, + SQLUSMALLINT *flags) +{ + int i; + SQLUSMALLINT exists[100]; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + for (i = 0; i < array_size(exists); i++) { + exists[i] = SQL_FALSE; + } + exists[SQL_API_SQLALLOCCONNECT] = SQL_TRUE; + exists[SQL_API_SQLFETCH] = SQL_TRUE; + exists[SQL_API_SQLALLOCENV] = SQL_TRUE; + exists[SQL_API_SQLFREECONNECT] = SQL_TRUE; + exists[SQL_API_SQLALLOCSTMT] = SQL_TRUE; + exists[SQL_API_SQLFREEENV] = SQL_TRUE; + exists[SQL_API_SQLBINDCOL] = SQL_TRUE; + exists[SQL_API_SQLFREESTMT] = SQL_TRUE; + exists[SQL_API_SQLCANCEL] = SQL_TRUE; + exists[SQL_API_SQLGETCURSORNAME] = SQL_TRUE; + exists[SQL_API_SQLCOLATTRIBUTES] = SQL_TRUE; + exists[SQL_API_SQLNUMRESULTCOLS] = SQL_TRUE; + exists[SQL_API_SQLCONNECT] = SQL_TRUE; + exists[SQL_API_SQLPREPARE] = SQL_TRUE; + exists[SQL_API_SQLDESCRIBECOL] = SQL_TRUE; + exists[SQL_API_SQLROWCOUNT] = SQL_TRUE; + exists[SQL_API_SQLDISCONNECT] = SQL_TRUE; + exists[SQL_API_SQLSETCURSORNAME] = SQL_FALSE; + exists[SQL_API_SQLERROR] = SQL_TRUE; + exists[SQL_API_SQLSETPARAM] = SQL_TRUE; + exists[SQL_API_SQLEXECDIRECT] = SQL_TRUE; + exists[SQL_API_SQLTRANSACT] = SQL_TRUE; + exists[SQL_API_SQLBULKOPERATIONS] = SQL_TRUE; + exists[SQL_API_SQLEXECUTE] = SQL_TRUE; + exists[SQL_API_SQLBINDPARAMETER] = SQL_TRUE; + exists[SQL_API_SQLGETTYPEINFO] = SQL_TRUE; + exists[SQL_API_SQLCOLUMNS] = SQL_TRUE; + exists[SQL_API_SQLPARAMDATA] = SQL_TRUE; + exists[SQL_API_SQLDRIVERCONNECT] = SQL_TRUE; + exists[SQL_API_SQLPUTDATA] = SQL_TRUE; + exists[SQL_API_SQLGETCONNECTOPTION] = SQL_TRUE; + exists[SQL_API_SQLSETCONNECTOPTION] = SQL_TRUE; + exists[SQL_API_SQLGETDATA] = SQL_TRUE; + exists[SQL_API_SQLSETSTMTOPTION] = SQL_TRUE; + exists[SQL_API_SQLGETFUNCTIONS] = SQL_TRUE; + exists[SQL_API_SQLSPECIALCOLUMNS] = SQL_TRUE; + exists[SQL_API_SQLGETINFO] = SQL_TRUE; + exists[SQL_API_SQLSTATISTICS] = SQL_TRUE; + exists[SQL_API_SQLGETSTMTOPTION] = SQL_TRUE; + exists[SQL_API_SQLTABLES] = SQL_TRUE; + exists[SQL_API_SQLBROWSECONNECT] = SQL_FALSE; + exists[SQL_API_SQLNUMPARAMS] = SQL_TRUE; + exists[SQL_API_SQLCOLUMNPRIVILEGES] = SQL_FALSE; + exists[SQL_API_SQLPARAMOPTIONS] = SQL_FALSE; + exists[SQL_API_SQLDATASOURCES] = SQL_TRUE; + exists[SQL_API_SQLPRIMARYKEYS] = SQL_TRUE; + exists[SQL_API_SQLDESCRIBEPARAM] = SQL_TRUE; + exists[SQL_API_SQLPROCEDURECOLUMNS] = SQL_TRUE; + exists[SQL_API_SQLDRIVERS] = SQL_FALSE; + exists[SQL_API_SQLPROCEDURES] = SQL_TRUE; + exists[SQL_API_SQLEXTENDEDFETCH] = SQL_TRUE; + exists[SQL_API_SQLSETPOS] = SQL_TRUE; + exists[SQL_API_SQLFOREIGNKEYS] = SQL_TRUE; + exists[SQL_API_SQLSETSCROLLOPTIONS] = SQL_TRUE; + exists[SQL_API_SQLMORERESULTS] = SQL_TRUE; + exists[SQL_API_SQLTABLEPRIVILEGES] = SQL_TRUE; + exists[SQL_API_SQLNATIVESQL] = SQL_TRUE; + if (func == SQL_API_ALL_FUNCTIONS) { + memcpy(flags, exists, sizeof (exists)); + } else if (func == SQL_API_ODBC3_ALL_FUNCTIONS) { + int i; +#define SET_EXISTS(x) \ + flags[(x) >> 4] |= (1 << ((x) & 0xF)) +#define CLR_EXISTS(x) \ + flags[(x) >> 4] &= ~(1 << ((x) & 0xF)) + + memset(flags, 0, + sizeof (SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE); + for (i = 0; i < array_size(exists); i++) { + if (exists[i]) { + flags[i >> 4] |= (1 << (i & 0xF)); + } + } + SET_EXISTS(SQL_API_SQLALLOCHANDLE); + SET_EXISTS(SQL_API_SQLFREEHANDLE); + SET_EXISTS(SQL_API_SQLGETSTMTATTR); + SET_EXISTS(SQL_API_SQLSETSTMTATTR); + SET_EXISTS(SQL_API_SQLGETCONNECTATTR); + SET_EXISTS(SQL_API_SQLSETCONNECTATTR); + SET_EXISTS(SQL_API_SQLGETENVATTR); + SET_EXISTS(SQL_API_SQLSETENVATTR); + SET_EXISTS(SQL_API_SQLCLOSECURSOR); + SET_EXISTS(SQL_API_SQLBINDPARAM); +#if !defined(HAVE_UNIXODBC) || !(HAVE_UNIXODBC) + /* + * Some unixODBC versions have problems with + * SQLError() vs. SQLGetDiagRec() with loss + * of error/warning messages. + */ + SET_EXISTS(SQL_API_SQLGETDIAGREC); +#endif + SET_EXISTS(SQL_API_SQLGETDIAGFIELD); + SET_EXISTS(SQL_API_SQLFETCHSCROLL); + SET_EXISTS(SQL_API_SQLENDTRAN); + } else { + if (func < array_size(exists)) { + *flags = exists[func]; + } else { + switch (func) { + case SQL_API_SQLALLOCHANDLE: + case SQL_API_SQLFREEHANDLE: + case SQL_API_SQLGETSTMTATTR: + case SQL_API_SQLSETSTMTATTR: + case SQL_API_SQLGETCONNECTATTR: + case SQL_API_SQLSETCONNECTATTR: + case SQL_API_SQLGETENVATTR: + case SQL_API_SQLSETENVATTR: + case SQL_API_SQLCLOSECURSOR: + case SQL_API_SQLBINDPARAM: +#if !defined(HAVE_UNIXODBC) || !(HAVE_UNIXODBC) + /* + * Some unixODBC versions have problems with + * SQLError() vs. SQLGetDiagRec() with loss + * of error/warning messages. + */ + case SQL_API_SQLGETDIAGREC: +#endif + case SQL_API_SQLGETDIAGFIELD: + case SQL_API_SQLFETCHSCROLL: + case SQL_API_SQLENDTRAN: + *flags = SQL_TRUE; + break; + default: + *flags = SQL_FALSE; + } + } + } + return SQL_SUCCESS; +} + +/** + * Internal allocate HENV. + * @param env pointer to environment handle + * @result ODBC error code + */ + +static SQLRETURN +drvallocenv(SQLHENV *env) +{ + ENV *e; + + if (env == NULL) { + return SQL_INVALID_HANDLE; + } + e = (ENV *) xmalloc(sizeof (ENV)); + if (e == NULL) { + *env = SQL_NULL_HENV; + return SQL_ERROR; + } + e->magic = ENV_MAGIC; + e->ov3 = 0; +#if defined(_WIN32) || defined(_WIN64) + InitializeCriticalSection(&e->cs); + e->owner = 0; +#endif + e->dbcs = NULL; + *env = (SQLHENV) e; + return SQL_SUCCESS; +} + +/** + * Allocate HENV. + * @param env pointer to environment handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLAllocEnv(SQLHENV *env) +{ + return drvallocenv(env); +} + +/** + * Internal free HENV. + * @param env environment handle + * @result ODBC error code + */ + +static SQLRETURN +drvfreeenv(SQLHENV env) +{ + ENV *e; + + if (env == SQL_NULL_HENV) { + return SQL_INVALID_HANDLE; + } + e = (ENV *) env; + if (e->magic != ENV_MAGIC) { + return SQL_SUCCESS; + } +#if defined(_WIN32) || defined(_WIN64) + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); +#endif + if (e->dbcs) { +#if defined(_WIN32) || defined(_WIN64) + LeaveCriticalSection(&e->cs); + e->owner = 0; +#endif + return SQL_ERROR; + } + e->magic = DEAD_MAGIC; +#if defined(_WIN32) || defined(_WIN64) + e->owner = 0; + LeaveCriticalSection(&e->cs); + DeleteCriticalSection(&e->cs); +#endif + xfree(e); + return SQL_SUCCESS; +} + +/** + * Free HENV. + * @param env environment handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFreeEnv(SQLHENV env) +{ + return drvfreeenv(env); +} + +/** + * Internal allocate HDBC. + * @param env environment handle + * @param dbc pointer to database connection handle + * @result ODBC error code + */ + +static SQLRETURN +drvallocconnect(SQLHENV env, SQLHDBC *dbc) +{ + DBC *d; + ENV *e; + const char *verstr; + int maj = 0, min = 0, lev = 0; + + if (dbc == NULL) { + return SQL_ERROR; + } + d = (DBC *) xmalloc(sizeof (DBC)); + if (d == NULL) { + *dbc = SQL_NULL_HDBC; + return SQL_ERROR; + } + memset(d, 0, sizeof (DBC)); + d->curtype = SQL_CURSOR_STATIC; + d->ov3 = &d->ov3val; + verstr = sqlite4_libversion(); + sscanf(verstr, "%d.%d.%d", &maj, &min, &lev); + d->version = verinfo(maj & 0xFF, min & 0xFF, lev & 0xFF); + e = (ENV *) env; +#if defined(_WIN32) || defined(_WIN64) + if (e->magic == ENV_MAGIC) { + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); + } +#endif + if (e->magic == ENV_MAGIC) { + DBC *n, *p; + + d->env = e; + d->ov3 = &e->ov3; + p = NULL; + n = e->dbcs; + while (n) { + p = n; + n = n->next; + } + if (p) { + p->next = d; + } else { + e->dbcs = d; + } + } +#if defined(_WIN32) || defined(_WIN64) + if (e->magic == ENV_MAGIC) { + e->owner = 0; + LeaveCriticalSection(&e->cs); + } + d->oemcp = 1; +#endif + d->autocommit = 1; + d->magic = DBC_MAGIC; + *dbc = (SQLHDBC) d; + drvgetgpps(d); + return SQL_SUCCESS; +} + +/** + * Allocate HDBC. + * @param env environment handle + * @param dbc pointer to database connection handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLAllocConnect(SQLHENV env, SQLHDBC *dbc) +{ + return drvallocconnect(env, dbc); +} + +/** + * Internal free connection (HDBC). + * @param dbc database connection handle + * @result ODBC error code + */ + +static SQLRETURN +drvfreeconnect(SQLHDBC dbc) +{ + DBC *d; + ENV *e; + SQLRETURN ret = SQL_ERROR; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (d->magic != DBC_MAGIC) { + return SQL_INVALID_HANDLE; + } + e = d->env; + if (e && e->magic == ENV_MAGIC) { +#if defined(_WIN32) || defined(_WIN64) + EnterCriticalSection(&e->cs); + e->owner = GetCurrentThreadId(); +#endif + } else { + e = NULL; + } + if (d->sqlite) { + setstatd(d, -1, "not disconnected", (*d->ov3) ? "HY000" : "S1000"); + goto done; + } + while (d->stmt) { + freestmt((HSTMT) d->stmt); + } + if (e && e->magic == ENV_MAGIC) { + DBC *n, *p; + + p = NULL; + n = e->dbcs; + while (n) { + if (n == d) { + break; + } + p = n; + n = n->next; + } + if (n) { + if (p) { + p->next = d->next; + } else { + e->dbcs = d->next; + } + } + } + drvrelgpps(d); + d->magic = DEAD_MAGIC; + if (d->trace) { + fclose(d->trace); + } + xfree(d); + ret = SQL_SUCCESS; +done: +#if defined(_WIN32) || defined(_WIN64) + if (e) { + e->owner = 0; + LeaveCriticalSection(&e->cs); + } +#endif + return ret; +} + +/** + * Free connection (HDBC). + * @param dbc database connection handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFreeConnect(SQLHDBC dbc) +{ + return drvfreeconnect(dbc); +} + +/** + * Internal get connect attribute of HDBC. + * @param dbc database connection handle + * @param attr option to be retrieved + * @param val output buffer + * @param bufmax size of output buffer + * @param buflen output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetconnectattr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + DBC *d; + SQLINTEGER dummy; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (!val) { + val = (SQLPOINTER) &dummy; + } + if (!buflen) { + buflen = &dummy; + } + switch (attr) { + case SQL_ATTR_CONNECTION_DEAD: + *((SQLINTEGER *) val) = d->sqlite ? SQL_CD_FALSE : SQL_CD_TRUE; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_ACCESS_MODE: + *((SQLINTEGER *) val) = SQL_MODE_READ_WRITE; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_AUTOCOMMIT: + *((SQLINTEGER *) val) = + d->autocommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_LOGIN_TIMEOUT: + *((SQLINTEGER *) val) = 100; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_ODBC_CURSORS: + *((SQLINTEGER *) val) = SQL_CUR_USE_DRIVER; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_PACKET_SIZE: + *((SQLINTEGER *) val) = 16384; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_TXN_ISOLATION: + *((SQLINTEGER *) val) = SQL_TXN_SERIALIZABLE; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_TRACEFILE: + case SQL_ATTR_TRANSLATE_LIB: + case SQL_ATTR_CURRENT_CATALOG: + *((SQLCHAR *) val) = 0; + *buflen = 0; + break; + case SQL_ATTR_TRACE: + case SQL_ATTR_QUIET_MODE: + case SQL_ATTR_TRANSLATE_OPTION: + case SQL_ATTR_KEYSET_SIZE: + case SQL_ATTR_QUERY_TIMEOUT: + *((SQLINTEGER *) val) = 0; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_PARAM_BIND_TYPE: + *((SQLULEN *) val) = SQL_PARAM_BIND_BY_COLUMN; + *buflen = sizeof (SQLUINTEGER); + break; + case SQL_ATTR_ROW_BIND_TYPE: + *((SQLULEN *) val) = SQL_BIND_BY_COLUMN; + *buflen = sizeof (SQLULEN); + break; + case SQL_ATTR_USE_BOOKMARKS: + *((SQLINTEGER *) val) = SQL_UB_OFF; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_ASYNC_ENABLE: + *((SQLINTEGER *) val) = SQL_ASYNC_ENABLE_OFF; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_NOSCAN: + *((SQLINTEGER *) val) = SQL_NOSCAN_ON; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_CONCURRENCY: + *((SQLINTEGER *) val) = SQL_CONCUR_LOCK; + *buflen = sizeof (SQLINTEGER); + break; +#ifdef SQL_ATTR_CURSOR_SENSITIVITY + case SQL_ATTR_CURSOR_SENSITIVITY: + *((SQLINTEGER *) val) = SQL_UNSPECIFIED; + *buflen = sizeof (SQLINTEGER); + break; +#endif + case SQL_ATTR_SIMULATE_CURSOR: + *((SQLINTEGER *) val) = SQL_SC_NON_UNIQUE; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_MAX_ROWS: + *((SQLINTEGER *) val) = 0; + *buflen = sizeof (SQLINTEGER); + case SQL_ATTR_MAX_LENGTH: + *((SQLINTEGER *) val) = 1000000000; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_CURSOR_TYPE: + *((SQLINTEGER *) val) = d->curtype; + *buflen = sizeof (SQLINTEGER); + break; + case SQL_ATTR_RETRIEVE_DATA: + *((SQLINTEGER *) val) = SQL_RD_ON; + *buflen = sizeof (SQLINTEGER); + break; +#ifdef SQL_ATTR_METADATA_ID + case SQL_ATTR_METADATA_ID: + *((SQLULEN *) val) = SQL_FALSE; + return SQL_SUCCESS; +#endif + default: + *((SQLINTEGER *) val) = 0; + *buflen = sizeof (SQLINTEGER); + setstatd(d, -1, "unsupported connect attribute %d", + (*d->ov3) ? "HYC00" : "S1C00", (int) attr); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Get connect attribute of HDBC. + * @param dbc database connection handle + * @param attr option to be retrieved + * @param val output buffer + * @param bufmax size of output buffer + * @param buflen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetConnectAttr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvgetconnectattr(dbc, attr, val, bufmax, buflen); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Get connect attribute of HDBC (UNICODE version). + * @param dbc database connection handle + * @param attr option to be retrieved + * @param val output buffer + * @param bufmax size of output buffer + * @param buflen output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetConnectAttrW(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER bufmax, SQLINTEGER *buflen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvgetconnectattr(dbc, attr, val, bufmax, buflen); + if (SQL_SUCCEEDED(ret)) { + switch (attr) { + case SQL_ATTR_TRACEFILE: + case SQL_ATTR_CURRENT_CATALOG: + case SQL_ATTR_TRANSLATE_LIB: + if (val && bufmax >= sizeof (SQLWCHAR)) { + *(SQLWCHAR *) val = 0; + } + break; + } + } + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +/** + * Internal set connect attribute of HDBC. + * @param dbc database connection handle + * @param attr option to be set + * @param val option value + * @param len size of option + * @result ODBC error code + */ + +static SQLRETURN +drvsetconnectattr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER len) +{ + DBC *d; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + switch (attr) { + case SQL_AUTOCOMMIT: + d->autocommit = val == (SQLPOINTER) SQL_AUTOCOMMIT_ON; + if (d->autocommit && d->intrans) { + return endtran(d, SQL_COMMIT, 1); + } else if (!d->autocommit) { + s4stmt_end(d->cur_s4stmt); + } + return SQL_SUCCESS; +#ifdef SQL_ATTR_METADATA_ID + case SQL_ATTR_METADATA_ID: + if (val == (SQLPOINTER) SQL_FALSE) { + return SQL_SUCCESS; + } + /* fall through */ +#endif + default: + setstatd(d, -1, "option value changed", "01S02"); + return SQL_SUCCESS_WITH_INFO; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Set connect attribute of HDBC. + * @param dbc database connection handle + * @param attr option to be set + * @param val option value + * @param len size of option + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetConnectAttr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER len) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvsetconnectattr(dbc, attr, val, len); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Set connect attribute of HDBC (UNICODE version). + * @param dbc database connection handle + * @param attr option to be set + * @param val option value + * @param len size of option + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetConnectAttrW(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val, + SQLINTEGER len) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvsetconnectattr(dbc, attr, val, len); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +/** + * Internal get connect option of HDBC. + * @param dbc database connection handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +static SQLRETURN +drvgetconnectoption(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param) +{ + DBC *d; + SQLINTEGER dummy; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (!param) { + param = (SQLPOINTER) &dummy; + } + switch (opt) { + case SQL_ACCESS_MODE: + *((SQLINTEGER *) param) = SQL_MODE_READ_WRITE; + break; + case SQL_AUTOCOMMIT: + *((SQLINTEGER *) param) = + d->autocommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; + break; + case SQL_LOGIN_TIMEOUT: + *((SQLINTEGER *) param) = 100; + break; + case SQL_ODBC_CURSORS: + *((SQLINTEGER *) param) = SQL_CUR_USE_DRIVER; + break; + case SQL_PACKET_SIZE: + *((SQLINTEGER *) param) = 16384; + break; + case SQL_TXN_ISOLATION: + *((SQLINTEGER *) param) = SQL_TXN_SERIALIZABLE; + break; + case SQL_OPT_TRACE: + case SQL_OPT_TRACEFILE: + case SQL_QUIET_MODE: + case SQL_TRANSLATE_DLL: + case SQL_TRANSLATE_OPTION: + case SQL_KEYSET_SIZE: + case SQL_QUERY_TIMEOUT: + case SQL_BIND_TYPE: + case SQL_CURRENT_QUALIFIER: + *((SQLINTEGER *) param) = 0; + break; + case SQL_USE_BOOKMARKS: + *((SQLINTEGER *) param) = SQL_UB_OFF; + break; + case SQL_ASYNC_ENABLE: + *((SQLINTEGER *) param) = SQL_ASYNC_ENABLE_OFF; + break; + case SQL_NOSCAN: + *((SQLINTEGER *) param) = SQL_NOSCAN_ON; + break; + case SQL_CONCURRENCY: + *((SQLINTEGER *) param) = SQL_CONCUR_LOCK; + break; + case SQL_SIMULATE_CURSOR: + *((SQLINTEGER *) param) = SQL_SC_NON_UNIQUE; + break; + case SQL_MAX_ROWS: + *((SQLINTEGER *) param) = 0; + break; + case SQL_ROWSET_SIZE: + case SQL_MAX_LENGTH: + *((SQLINTEGER *) param) = 1000000000; + break; + case SQL_CURSOR_TYPE: + *((SQLINTEGER *) param) = d->curtype; + break; + case SQL_RETRIEVE_DATA: + *((SQLINTEGER *) param) = SQL_RD_ON; + break; + default: + *((SQLINTEGER *) param) = 0; + setstatd(d, -1, "unsupported connect option %d", + (*d->ov3) ? "HYC00" : "S1C00", opt); + return SQL_ERROR; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Get connect option of HDBC. + * @param dbc database connection handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetConnectOption(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvgetconnectoption(dbc, opt, param); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Get connect option of HDBC (UNICODE version). + * @param dbc database connection handle + * @param opt option to be retrieved + * @param param output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetConnectOptionW(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvgetconnectoption(dbc, opt, param); + if (SQL_SUCCEEDED(ret)) { + switch (opt) { + case SQL_OPT_TRACEFILE: + case SQL_CURRENT_QUALIFIER: + case SQL_TRANSLATE_DLL: + if (param) { + *(SQLWCHAR *) param = 0; + } + break; + } + } + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +/** + * Internal set option on HDBC. + * @param dbc database connection handle + * @param opt option to be set + * @param param option value + * @result ODBC error code + */ + +static SQLRETURN +drvsetconnectoption(SQLHDBC dbc, SQLUSMALLINT opt, SQLUINTEGER param) +{ + DBC *d; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + switch (opt) { + case SQL_AUTOCOMMIT: + d->autocommit = param == SQL_AUTOCOMMIT_ON; + if (d->autocommit && d->intrans) { + return endtran(d, SQL_COMMIT, 1); + } else if (!d->autocommit) { + s4stmt_end(d->cur_s4stmt); + } + break; + default: + setstatd(d, -1, "option value changed", "01S02"); + return SQL_SUCCESS_WITH_INFO; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Set option on HDBC. + * @param dbc database connection handle + * @param opt option to be set + * @param param option value + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetConnectOption(SQLHDBC dbc, SQLUSMALLINT opt, SQLULEN param) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvsetconnectoption(dbc, opt, param); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Set option on HDBC (UNICODE version). + * @param dbc database connection handle + * @param opt option to be set + * @param param option value + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetConnectOptionW(SQLHDBC dbc, SQLUSMALLINT opt, SQLULEN param) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvsetconnectoption(dbc, opt, param); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#if defined(WITHOUT_DRIVERMGR) || (!defined(_WIN32) && !defined(_WIN64)) + +/** + * Handling of SQLConnect() connection attributes + * for standalone operation without driver manager. + * @param dsn DSN/driver connection string + * @param attr attribute string to be retrieved + * @param out output buffer + * @param outLen length of output buffer + * @result true or false + */ + +static int +getdsnattr(char *dsn, char *attr, char *out, int outLen) +{ + char *str = dsn, *start; + int len = strlen(attr); + + while (*str) { + while (*str && *str == ';') { + ++str; + } + start = str; + if ((str = strchr(str, '=')) == NULL) { + return 0; + } + if (str - start == len && strncasecmp(start, attr, len) == 0) { + start = ++str; + while (*str && *str != ';') { + ++str; + } + len = min(outLen - 1, str - start); + strncpy(out, start, len); + out[len] = '\0'; + return 1; + } + while (*str && *str != ';') { + ++str; + } + } + return 0; +} +#endif + +/** + * Internal connect to SQLite database. + * @param dbc database connection handle + * @param dsn DSN string + * @param dsnLen length of DSN string or SQL_NTS + * @param pwd password or NULL + * @param pwdLen length of password or SQL_NTS + * @param isu true/false: file name is UTF8 encoded + * @result ODBC error code + */ + +static SQLRETURN +drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, char *pwd, + int pwdLen, int isu) +{ + DBC *d; + int len; + SQLRETURN ret; + char buf[SQL_MAX_MESSAGE_LENGTH], dbname[SQL_MAX_MESSAGE_LENGTH / 4]; + char busy[SQL_MAX_MESSAGE_LENGTH / 4], tracef[SQL_MAX_MESSAGE_LENGTH]; + char loadext[SQL_MAX_MESSAGE_LENGTH]; + char sflag[32], spflag[32], ntflag[32], nwflag[32], biflag[32]; + char snflag[32], lnflag[32], ncflag[32], fkflag[32], jmode[32]; +#if defined(_WIN32) || defined(_WIN64) + char oemcp[32]; +#endif + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (d->magic != DBC_MAGIC) { + return SQL_INVALID_HANDLE; + } + if (d->sqlite != NULL) { + setstatd(d, -1, "connection already established", "08002"); + return SQL_ERROR; + } + buf[0] = '\0'; + if (dsnLen == SQL_NTS) { + len = sizeof (buf) - 1; + } else { + len = min(sizeof (buf) - 1, dsnLen); + } + if (dsn != NULL) { + strncpy(buf, (char *) dsn, len); + } + buf[len] = '\0'; + if (buf[0] == '\0') { + setstatd(d, -1, "invalid DSN", (*d->ov3) ? "HY090" : "S1090"); + return SQL_ERROR; + } +#if defined(_WIN32) || defined(_WIN64) + /* + * When DSN is in UTF it must be converted to ANSI + * here for ANSI SQLGetPrivateProfileString() + */ + if (isu) { + char *cdsn = utf_to_wmb(buf, len); + + if (!cdsn) { + setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + strcpy(buf, cdsn); + uc_free(cdsn); + } +#endif + busy[0] = '\0'; + dbname[0] = '\0'; +#ifdef WITHOUT_DRIVERMGR + getdsnattr(buf, "database", dbname, sizeof (dbname)); + if (dbname[0] == '\0') { + strncpy(dbname, buf, sizeof (dbname)); + dbname[sizeof (dbname) - 1] = '\0'; + } + getdsnattr(buf, "timeout", busy, sizeof (busy)); + sflag[0] = '\0'; + getdsnattr(buf, "stepapi", sflag, sizeof (sflag)); + spflag[0] = '\0'; + getdsnattr(buf, "syncpragma", spflag, sizeof (spflag)); + ntflag[0] = '\0'; + getdsnattr(buf, "notxn", ntflag, sizeof (ntflag)); + nwflag[0] = '\0'; + getdsnattr(buf, "nowchar", nwflag, sizeof (nwflag)); + snflag[0] = '\0'; + getdsnattr(buf, "shortnames", snflag, sizeof (snflag)); + lnflag[0] = '\0'; + getdsnattr(buf, "longnames", lnflag, sizeof (lnflag)); + ncflag[0] = '\0'; + getdsnattr(buf, "nocreat", ncflag, sizeof (ncflag)); + fkflag[0] = '\0'; + getdsnattr(buf, "fksupport", fkflag, sizeof (fkflag)); + loadext[0] = '\0'; + getdsnattr(buf, "loadext", loadext, sizeof (loadext)); + jmode[0] = '\0'; + getdsnattr(buf, "journalmode", jmode, sizeof (jmode)); +#if defined(_WIN32) || defined(_WIN64) + oemcp[0] = '\0'; + getdsnattr(buf, "oemcp", oemcp, sizeof (oemcp)); +#endif + biflag[0] = '\0'; + getdsnattr(buf, "bigint", biflag, sizeof (biflag)); +#else + SQLGetPrivateProfileString(buf, "timeout", "100000", + busy, sizeof (busy), ODBC_INI); + SQLGetPrivateProfileString(buf, "database", "", + dbname, sizeof (dbname), ODBC_INI); +#if defined(_WIN32) || defined(_WIN64) + /* database name read from registry is not UTF8 !!! */ + isu = 0; +#endif + SQLGetPrivateProfileString(buf, "stepapi", "", + sflag, sizeof (sflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "syncpragma", "NORMAL", + spflag, sizeof (spflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "notxn", "", + ntflag, sizeof (ntflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "nowchar", "", + nwflag, sizeof (nwflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "shortnames", "", + snflag, sizeof (snflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "longnames", "", + lnflag, sizeof (lnflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "nocreat", "", + ncflag, sizeof (ncflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "fksupport", "", + fkflag, sizeof (fkflag), ODBC_INI); + SQLGetPrivateProfileString(buf, "loadext", "", + loadext, sizeof (loadext), ODBC_INI); + SQLGetPrivateProfileString(buf, "journalmode", "", + jmode, sizeof (jmode), ODBC_INI); +#if defined(_WIN32) || defined(_WIN64) + SQLGetPrivateProfileString(buf, "oemcp", "1", + oemcp, sizeof (oemcp), ODBC_INI); +#endif + SQLGetPrivateProfileString(buf, "bigint", "", + biflag, sizeof (biflag), ODBC_INI); +#endif + tracef[0] = '\0'; +#ifdef WITHOUT_DRIVERMGR + getdsnattr(buf, "tracefile", tracef, sizeof (tracef)); +#else + SQLGetPrivateProfileString(buf, "tracefile", "", + tracef, sizeof (tracef), ODBC_INI); +#endif + if (tracef[0] != '\0') { + d->trace = fopen(tracef, "a"); + } + d->nowchar = getbool(nwflag); + d->shortnames = getbool(snflag); + d->longnames = getbool(lnflag); + d->nocreat = getbool(ncflag); + d->fksupport = getbool(fkflag); +#if defined(_WIN32) || defined(_WIN64) + d->oemcp = getbool(oemcp); +#else + d->oemcp = 0; +#endif + d->dobigint = getbool(biflag); + d->pwd = pwd; + d->pwdLen = 0; + if (d->pwd) { + d->pwdLen = (pwdLen == SQL_NTS) ? strlen(d->pwd) : pwdLen; + } + ret = dbopen(d, dbname, isu, (char *) dsn, sflag, spflag, ntflag, + jmode, busy); + if (ret == SQL_SUCCESS) { + dbloadext(d, loadext); + } + return ret; +} + +#ifndef WINTERFACE +/** + * Connect to SQLite database. + * @param dbc database connection handle + * @param dsn DSN string + * @param dsnLen length of DSN string or SQL_NTS + * @param uid user id string or NULL + * @param uidLen length of user id string or SQL_NTS + * @param pwd password string or NULL + * @param pwdLen length of password string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLConnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, + SQLCHAR *uid, SQLSMALLINT uidLen, + SQLCHAR *pwd, SQLSMALLINT pwdLen) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvconnect(dbc, dsn, dsnLen, (char *) pwd, pwdLen, 0); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Connect to SQLite database. + * @param dbc database connection handle + * @param dsn DSN string + * @param dsnLen length of DSN string or SQL_NTS + * @param uid user id string or NULL + * @param uidLen length of user id string or SQL_NTS + * @param pwd password string or NULL + * @param pwdLen length of password string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLConnectW(SQLHDBC dbc, SQLWCHAR *dsn, SQLSMALLINT dsnLen, + SQLWCHAR *uid, SQLSMALLINT uidLen, + SQLWCHAR *pwd, SQLSMALLINT pwdLen) +{ + char *dsna = NULL; + char *pwda = NULL; + SQLRETURN ret; + + HDBC_LOCK(dbc); + if (dsn) { + dsna = uc_to_utf_c(dsn, dsnLen); + if (!dsna) { + DBC *d = (DBC *) dbc; + + setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000"); + ret = SQL_ERROR; + goto done; + } + } + if (pwd) { + pwda = uc_to_utf_c(pwd, pwdLen); + if (!pwda) { + DBC *d = (DBC *) dbc; + + setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000"); + ret = SQL_ERROR; + goto done; + } + } + ret = drvconnect(dbc, (SQLCHAR *) dsna, SQL_NTS, pwda, SQL_NTS, 1); +done: + HDBC_UNLOCK(dbc); + uc_free(dsna); + uc_free(pwda); + return ret; +} +#endif + +/** + * Internal disconnect given HDBC. + * @param dbc database connection handle + * @result ODBC error code + */ + +static SQLRETURN +drvdisconnect(SQLHDBC dbc) +{ + DBC *d; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (d->magic != DBC_MAGIC) { + return SQL_INVALID_HANDLE; + } + if (d->intrans) { + setstatd(d, -1, "incomplete transaction", "25000"); + return SQL_ERROR; + } + if (d->cur_s4stmt) { + s4stmt_end(d->cur_s4stmt); + } + if (d->sqlite) { + if (d->trace) { + fprintf(d->trace, "-- sqlite4_close: '%s'\n", + d->dbname); + fflush(d->trace); + } + sqlite4_close(d->sqlite, 0); + d->sqlite = NULL; + } + freep(&d->dbname); + freep(&d->dsn); + return SQL_SUCCESS; +} + +/** + * Disconnect given HDBC. + * @param dbc database connection handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDisconnect(SQLHDBC dbc) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvdisconnect(dbc); + HDBC_UNLOCK(dbc); + return ret; +} + +#if defined(WITHOUT_DRIVERMGR) || (!defined(_WIN32) && !defined(_WIN64)) + +/** + * Internal standalone (w/o driver manager) database connect. + * @param dbc database connection handle + * @param hwnd dummy window handle or NULL + * @param connIn driver connect input string + * @param connInLen length of driver connect input string or SQL_NTS + * @param connOut driver connect output string + * @param connOutMax length of driver connect output string + * @param connOutLen output length of driver connect output string + * @param drvcompl completion type + * @result ODBC error code + */ + +static SQLRETURN +drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, + SQLCHAR *connIn, SQLSMALLINT connInLen, + SQLCHAR *connOut, SQLSMALLINT connOutMax, + SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) +{ + DBC *d; + int len; + SQLRETURN ret; + char buf[SQL_MAX_MESSAGE_LENGTH * 6], dbname[SQL_MAX_MESSAGE_LENGTH]; + char dsn[SQL_MAX_MESSAGE_LENGTH / 4], busy[SQL_MAX_MESSAGE_LENGTH / 4]; + char tracef[SQL_MAX_MESSAGE_LENGTH], loadext[SQL_MAX_MESSAGE_LENGTH]; + char pwd[SQL_MAX_MESSAGE_LENGTH]; + char sflag[32], spflag[32], ntflag[32], snflag[32], lnflag[32]; + char ncflag[32], nwflag[32], fkflag[32], jmode[32], biflag[32]; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + if (drvcompl != SQL_DRIVER_COMPLETE && + drvcompl != SQL_DRIVER_COMPLETE_REQUIRED && + drvcompl != SQL_DRIVER_PROMPT && + drvcompl != SQL_DRIVER_NOPROMPT) { + return SQL_NO_DATA; + } + d = (DBC *) dbc; + if (d->sqlite) { + setstatd(d, -1, "connection already established", "08002"); + return SQL_ERROR; + } + buf[0] = '\0'; + if (connInLen == SQL_NTS) { + len = sizeof (buf) - 1; + } else { + len = min(connInLen, sizeof (buf) - 1); + } + if (connIn != NULL) { + strncpy(buf, (char *) connIn, len); + } + buf[len] = '\0'; + if (!buf[0]) { + setstatd(d, -1, "invalid connect attributes", + (*d->ov3) ? "HY090" : "S1090"); + return SQL_ERROR; + } + dsn[0] = '\0'; + getdsnattr(buf, "DSN", dsn, sizeof (dsn)); + + /* special case: connIn is sole DSN value without keywords */ + if (!dsn[0] && !strchr(buf, ';') && !strchr(buf, '=')) { + strncpy(dsn, buf, sizeof (dsn) - 1); + dsn[sizeof (dsn) - 1] = '\0'; + } + + busy[0] = '\0'; + getdsnattr(buf, "timeout", busy, sizeof (busy)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !busy[0]) { + SQLGetPrivateProfileString(dsn, "timeout", "100000", + busy, sizeof (busy), ODBC_INI); + } +#endif + dbname[0] = '\0'; + getdsnattr(buf, "database", dbname, sizeof (dbname)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !dbname[0]) { + SQLGetPrivateProfileString(dsn, "database", "", + dbname, sizeof (dbname), ODBC_INI); + } +#endif + sflag[0] = '\0'; + getdsnattr(buf, "stepapi", sflag, sizeof (sflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !sflag[0]) { + SQLGetPrivateProfileString(dsn, "stepapi", "", + sflag, sizeof (sflag), ODBC_INI); + } +#endif + spflag[0] = '\0'; + getdsnattr(buf, "syncpragma", spflag, sizeof (spflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !spflag[0]) { + SQLGetPrivateProfileString(dsn, "syncpragma", "NORMAL", + spflag, sizeof (spflag), ODBC_INI); + } +#endif + ntflag[0] = '\0'; + getdsnattr(buf, "notxn", ntflag, sizeof (ntflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !ntflag[0]) { + SQLGetPrivateProfileString(dsn, "notxn", "", + ntflag, sizeof (ntflag), ODBC_INI); + } +#endif + snflag[0] = '\0'; + getdsnattr(buf, "shortnames", snflag, sizeof (snflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !snflag[0]) { + SQLGetPrivateProfileString(dsn, "shortnames", "", + snflag, sizeof (snflag), ODBC_INI); + } +#endif + lnflag[0] = '\0'; + getdsnattr(buf, "longnames", lnflag, sizeof (lnflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !lnflag[0]) { + SQLGetPrivateProfileString(dsn, "longnames", "", + lnflag, sizeof (lnflag), ODBC_INI); + } +#endif + ncflag[0] = '\0'; + getdsnattr(buf, "nocreat", ncflag, sizeof (ncflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !ncflag[0]) { + SQLGetPrivateProfileString(dsn, "nocreat", "", + ncflag, sizeof (ncflag), ODBC_INI); + } +#endif + nwflag[0] = '\0'; + getdsnattr(buf, "nowchar", nwflag, sizeof (nwflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !nwflag[0]) { + SQLGetPrivateProfileString(dsn, "nowchar", "", + nwflag, sizeof (nwflag), ODBC_INI); + } +#endif + fkflag[0] = '\0'; + getdsnattr(buf, "fksupport", fkflag, sizeof (fkflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !fkflag[0]) { + SQLGetPrivateProfileString(dsn, "fksupport", "", + fkflag, sizeof (fkflag), ODBC_INI); + } +#endif + loadext[0] = '\0'; + getdsnattr(buf, "loadext", loadext, sizeof (loadext)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !loadext[0]) { + SQLGetPrivateProfileString(dsn, "loadext", "", + loadext, sizeof (loadext), ODBC_INI); + } +#endif + jmode[0] = '\0'; + getdsnattr(buf, "journalmode", jmode, sizeof (jmode)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !jmode[0]) { + SQLGetPrivateProfileString(dsn, "journalmode", "", + jmode, sizeof (jmode), ODBC_INI); + } +#endif + biflag[0] = '\0'; + getdsnattr(buf, "bigint", biflag, sizeof (biflag)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !biflag[0]) { + SQLGetPrivateProfileString(dsn, "bigint", "", + biflag, sizeof (biflag), ODBC_INI); + } +#endif + pwd[0] = '\0'; + getdsnattr(buf, "pwd", pwd, sizeof (pwd)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !pwd[0]) { + SQLGetPrivateProfileString(dsn, "pwd", "", + pwd, sizeof (pwd), ODBC_INI); + } +#endif + + if (!dbname[0] && !dsn[0]) { + strcpy(dsn, "SQLite"); + strncpy(dbname, buf, sizeof (dbname)); + dbname[sizeof (dbname) - 1] = '\0'; + } + tracef[0] = '\0'; + getdsnattr(buf, "tracefile", tracef, sizeof (tracef)); +#ifndef WITHOUT_DRIVERMGR + if (dsn[0] && !tracef[0]) { + SQLGetPrivateProfileString(dsn, "tracefile", "", + tracef, sizeof (tracef), ODBC_INI); + } +#endif + if (connOut || connOutLen) { + int count; + + buf[0] = '\0'; + count = snprintf(buf, sizeof (buf), + "DSN=%s;Database=%s;StepAPI=%s;Timeout=%s;" + "SyncPragma=%s;NoTXN=%s;ShortNames=%s;LongNames=%s;" + "NoCreat=%s;NoWCHAR=%s;FKSupport=%s;Tracefile=%s;" + "JournalMode=%s;LoadExt=%s;BigInt=%s;PWD=%s", + dsn, dbname, sflag, busy, spflag, ntflag, + snflag, lnflag, ncflag, nwflag, fkflag, tracef, + jmode, loadext, biflag,pwd); + if (count < 0) { + buf[sizeof (buf) - 1] = '\0'; + } + len = min(connOutMax - 1, strlen(buf)); + if (connOut) { + strncpy((char *) connOut, buf, len); + connOut[len] = '\0'; + } + if (connOutLen) { + *connOutLen = len; + } + } + if (tracef[0] != '\0') { + d->trace = fopen(tracef, "a"); + } + d->shortnames = getbool(snflag); + d->longnames = getbool(lnflag); + d->nocreat = getbool(ncflag); + d->nowchar = getbool(nwflag); + d->fksupport = getbool(fkflag); + d->dobigint = getbool(biflag); + d->oemcp = 0; + d->pwdLen = strlen(pwd); + d->pwd = (d->pwdLen > 0) ? pwd : NULL; + ret = dbopen(d, dbname, 0, dsn, sflag, spflag, ntflag, jmode, busy); + memset(pwd, 0, sizeof (pwd)); + if (ret == SQL_SUCCESS) { + dbloadext(d, loadext); + } + return ret; +} +#endif + +/** + * Internal free function for HSTMT. + * @param stmt statement handle + * @result ODBC error code + */ + +static SQLRETURN +freestmt(SQLHSTMT stmt) +{ + STMT *s; + DBC *d; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + s4stmt_drop(s); + freeresult(s, 1); + freep(&s->query); + d = (DBC *) s->dbc; + if (d && d->magic == DBC_MAGIC) { + STMT *p, *n; + + p = NULL; + n = d->stmt; + while (n) { + if (n == s) { + break; + } + p = n; + n = n->next; + } + if (n) { + if (p) { + p->next = s->next; + } else { + d->stmt = s->next; + } + } + } + freeparams(s); + freep(&s->bindparms); + if (s->row_status0 != &s->row_status1) { + freep(&s->row_status0); + s->rowset_size = 1; + s->row_status0 = &s->row_status1; + } + xfree(s); + return SQL_SUCCESS; +} + +/** + * Allocate HSTMT given HDBC (driver internal version). + * @param dbc database connection handle + * @param stmt pointer to statement handle + * @result ODBC error code + */ + +static SQLRETURN +drvallocstmt(SQLHDBC dbc, SQLHSTMT *stmt) +{ + DBC *d; + STMT *s, *sl, *pl; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (d->magic != DBC_MAGIC || stmt == NULL) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) xmalloc(sizeof (STMT)); + if (s == NULL) { + *stmt = SQL_NULL_HSTMT; + return SQL_ERROR; + } + *stmt = (SQLHSTMT) s; + memset(s, 0, sizeof (STMT)); + s->dbc = dbc; + s->ov3 = d->ov3; + s->bkmrk = SQL_UB_OFF; + s->bkmrkptr = 0; + s->oemcp = &d->oemcp; + s->nowchar[0] = d->nowchar; + s->nowchar[1] = 0; + s->dobigint = d->dobigint; + s->curtype = d->curtype; + s->row_status0 = &s->row_status1; + s->rowset_size = 1; + s->longnames = d->longnames; + s->retr_data = SQL_RD_ON; + s->max_rows = 0; + s->bind_type = SQL_BIND_BY_COLUMN; + s->bind_offs = NULL; + s->paramset_size = 1; + s->parm_bind_type = SQL_PARAM_BIND_BY_COLUMN; + s->one_tbl = -1; + s->has_pk = -1; + s->has_rowid = -1; +#ifdef _WIN64 + sprintf((char *) s->cursorname, "CUR_%I64X", (SQLUBIGINT) *stmt); +#else + sprintf((char *) s->cursorname, "CUR_%016lX", (long) *stmt); +#endif + sl = d->stmt; + pl = NULL; + while (sl) { + pl = sl; + sl = sl->next; + } + if (pl) { + pl->next = s; + } else { + d->stmt = s; + } + return SQL_SUCCESS; +} + +/** + * Allocate HSTMT given HDBC. + * @param dbc database connection handle + * @param stmt pointer to statement handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLAllocStmt(SQLHDBC dbc, SQLHSTMT *stmt) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvallocstmt(dbc, stmt); + HDBC_UNLOCK(dbc); + return ret; +} + +/** + * Internal function to perform certain kinds of free/close on STMT. + * @param stmt statement handle + * @param opt SQL_RESET_PARAMS, SQL_UNBIND, SQL_CLOSE, or SQL_DROP + * @result ODBC error code + */ + +static SQLRETURN +drvfreestmt(SQLHSTMT stmt, SQLUSMALLINT opt) +{ + STMT *s; + SQLRETURN ret = SQL_SUCCESS; + SQLHDBC dbc; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + HSTMT_LOCK(stmt); + s = (STMT *) stmt; + dbc = s->dbc; + switch (opt) { + case SQL_RESET_PARAMS: + freeparams(s); + break; + case SQL_UNBIND: + unbindcols(s); + break; + case SQL_CLOSE: + s4stmt_end_if(s); + freeresult(s, 0); + break; + case SQL_DROP: + s4stmt_end_if(s); + ret = freestmt(stmt); + break; + default: + setstat(s, -1, "unsupported option", (*s->ov3) ? "HYC00" : "S1C00"); + ret = SQL_ERROR; + break; + } + HDBC_UNLOCK(dbc); + return ret; +} + +/** + * Free HSTMT. + * @param stmt statement handle + * @param opt SQL_RESET_PARAMS, SQL_UNBIND, SQL_CLOSE, or SQL_DROP + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFreeStmt(SQLHSTMT stmt, SQLUSMALLINT opt) +{ + return drvfreestmt(stmt, opt); +} + +/** + * Cancel HSTMT closing cursor. + * @param stmt statement handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLCancel(SQLHSTMT stmt) +{ + if (stmt != SQL_NULL_HSTMT) { + DBC *d = (DBC *) ((STMT *) stmt)->dbc; +#if defined(_WIN32) || defined(_WIN64) + /* interrupt when other thread owns critical section */ + int i; + + for (i = 0; i < 2; i++) { + if (d->magic == DBC_MAGIC && d->env && + d->env->magic == ENV_MAGIC && + d->env->owner != GetCurrentThreadId() && + d->env->owner != 0) { + d->busyint = 1; + sqlite4_interrupt(d->sqlite); + } + Sleep(1); + } + +#else + if (d->magic == DBC_MAGIC) { + d->busyint = 1; + sqlite4_interrupt(d->sqlite); + } +#endif + } + return drvfreestmt(stmt, SQL_CLOSE); +} + +/** + * Internal function to get cursor name of STMT. + * @param stmt statement handle + * @param cursor output buffer + * @param buflen length of output buffer + * @param lenp output length + * @result ODBC error code + */ + +static SQLRETURN +drvgetcursorname(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT buflen, + SQLSMALLINT *lenp) +{ + STMT *s; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (lenp && !cursor) { + *lenp = strlen((char *) s->cursorname); + return SQL_SUCCESS; + } + if (cursor) { + if (buflen > 0) { + strncpy((char *) cursor, (char *) s->cursorname, buflen - 1); + cursor[buflen - 1] = '\0'; + } + if (lenp) { + *lenp = min(strlen((char *) s->cursorname), buflen - 1); + } + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Get cursor name of STMT. + * @param stmt statement handle + * @param cursor output buffer + * @param buflen length of output buffer + * @param lenp output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetCursorName(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT buflen, + SQLSMALLINT *lenp) +{ + SQLRETURN ret; +#if defined(_WIN32) || defined(_WIN64) + SQLSMALLINT len = 0; +#endif + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvgetcursorname(stmt, cursor, buflen, lenp); + goto done; + } + ret = drvgetcursorname(stmt, cursor, buflen, &len); + if (ret == SQL_SUCCESS) { + char *c = NULL; + + if (cursor) { + c = utf_to_wmb((char *) cursor, len); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + c[len] = 0; + len = strlen(c); + if (buflen > 0) { + strncpy((char *) cursor, c, buflen - 1); + cursor[buflen - 1] = 0; + } + uc_free(c); + } + if (lenp) { + *lenp = min(len, buflen - 1); + } + } +done: + ; +#else + ret = drvgetcursorname(stmt, cursor, buflen, lenp); +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Get cursor name of STMT (UNICODE version). + * @param stmt statement handle + * @param cursor output buffer + * @param buflen length of output buffer + * @param lenp output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetCursorNameW(SQLHSTMT stmt, SQLWCHAR *cursor, SQLSMALLINT buflen, + SQLSMALLINT *lenp) +{ + SQLRETURN ret; + SQLSMALLINT len = 0; + + HSTMT_LOCK(stmt); + ret = drvgetcursorname(stmt, (SQLCHAR *) cursor, buflen, &len); + if (ret == SQL_SUCCESS) { + SQLWCHAR *c = NULL; + + if (cursor) { + c = uc_from_utf((SQLCHAR *) cursor, len); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + c[len] = 0; + len = uc_strlen(c); + if (buflen > 0) { + uc_strncpy(cursor, c, buflen - 1); + cursor[buflen - 1] = 0; + } + uc_free(c); + } + if (lenp) { + *lenp = min(len, buflen - 1); + } + } +done: + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal function to set cursor name on STMT. + * @param stmt statement handle + * @param cursor new cursor name + * @param len length of cursor name or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvsetcursorname(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT len) +{ + STMT *s; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!cursor || + !((cursor[0] >= 'A' && cursor[0] <= 'Z') || + (cursor[0] >= 'a' && cursor[0] <= 'z'))) { + setstat(s, -1, "invalid cursor name", (*s->ov3) ? "HYC00" : "S1C00"); + return SQL_ERROR; + } + if (len == SQL_NTS) { + len = sizeof (s->cursorname) - 1; + } else { + len = min(sizeof (s->cursorname) - 1, len); + } + strncpy((char *) s->cursorname, (char *) cursor, len); + s->cursorname[len] = '\0'; + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Set cursor name on STMT. + * @param stmt statement handle + * @param cursor new cursor name + * @param len length of cursor name or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetCursorName(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT len) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvsetcursorname(stmt, cursor, len); + goto done2; + } + if (cursor) { + c = wmb_to_utf_c((char *) cursor, len); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvsetcursorname(stmt, (SQLCHAR *) c, SQL_NTS); +#else + ret = drvsetcursorname(stmt, cursor, len); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Set cursor name on STMT (UNICODE version). + * @param stmt statement handle + * @param cursor new cursor name + * @param len length of cursor name or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLSetCursorNameW(SQLHSTMT stmt, SQLWCHAR *cursor, SQLSMALLINT len) +{ + char *c = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cursor) { + c = uc_to_utf_c(cursor, len); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvsetcursorname(stmt, (SQLCHAR *) c, SQL_NTS); +done: + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Close open cursor. + * @param stmt statement handle + * @return ODBC error code + */ + +SQLRETURN SQL_API +SQLCloseCursor(SQLHSTMT stmt) +{ + return drvfreestmt(stmt, SQL_CLOSE); +} + +/** + * Allocate a HENV, HDBC, or HSTMT handle. + * @param type handle type + * @param input input handle (HENV, HDBC) + * @param output pointer to output handle (HENV, HDBC, HSTMT) + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLAllocHandle(SQLSMALLINT type, SQLHANDLE input, SQLHANDLE *output) +{ + SQLRETURN ret; + + switch (type) { + case SQL_HANDLE_ENV: + ret = drvallocenv((SQLHENV *) output); + if (ret == SQL_SUCCESS) { + ENV *e = (ENV *) *output; + + if (e && e->magic == ENV_MAGIC) { + e->ov3 = 1; + } + } + return ret; + case SQL_HANDLE_DBC: + return drvallocconnect((SQLHENV) input, (SQLHDBC *) output); + case SQL_HANDLE_STMT: + HDBC_LOCK((SQLHDBC) input); + ret = drvallocstmt((SQLHDBC) input, (SQLHSTMT *) output); + HDBC_UNLOCK((SQLHDBC) input); + return ret; + } + return SQL_ERROR; +} + +/** + * Free a HENV, HDBC, or HSTMT handle. + * @param type handle type + * @param h handle (HENV, HDBC, or HSTMT) + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFreeHandle(SQLSMALLINT type, SQLHANDLE h) +{ + switch (type) { + case SQL_HANDLE_ENV: + return drvfreeenv((SQLHENV) h); + case SQL_HANDLE_DBC: + return drvfreeconnect((SQLHDBC) h); + case SQL_HANDLE_STMT: + return drvfreestmt((SQLHSTMT) h, SQL_DROP); + } + return SQL_ERROR; +} + +/** + * Free dynamically allocated column descriptions of STMT. + * @param s statement pointer + */ + +static void +freedyncols(STMT *s) +{ + if (s->dyncols) { + int i; + + for (i = 0; i < s->dcols; i++) { + freep(&s->dyncols[i].typename); + } + if (s->cols == s->dyncols) { + s->cols = NULL; + s->ncols = 0; + s->one_tbl = -1; + s->has_pk = -1; + } + freep(&s->dyncols); + } + s->dcols = 0; +} + +/** + * Free statement's result. + * @param s statement pointer + * @param clrcols flag to clear column information + * + * The result rows are free'd using the rowfree function pointer. + * If clrcols is greater than zero, then column bindings and dynamic column + * descriptions are free'd. + * If clrcols is less than zero, then dynamic column descriptions are free'd. + */ + +static void +freeresult(STMT *s, int clrcols) +{ + freep(&s->bincache); + s->bincell = NULL; + s->binlen = 0; + if (s->rows) { + if (s->rowfree) { + s->rowfree(s->rows); + s->rowfree = NULL; + } + s->rows = NULL; + } + s->nrows = -1; + if (clrcols > 0) { + freep(&s->bindcols); + s->nbindcols = 0; + } + if (clrcols) { + freedyncols(s); + s->cols = NULL; + s->ncols = 0; + s->nowchar[1] = 0; + s->one_tbl = -1; + s->has_pk = -1; + s->has_rowid = -1; + } +} + +/** + * Reset bound columns to unbound state. + * @param s statement pointer + */ + +static void +unbindcols(STMT *s) +{ + int i; + + for (i = 0; s->bindcols && i < s->nbindcols; i++) { + s->bindcols[i].type = SQL_UNKNOWN_TYPE; + s->bindcols[i].max = 0; + s->bindcols[i].lenp = NULL; + s->bindcols[i].valp = NULL; + s->bindcols[i].index = i; + s->bindcols[i].offs = 0; + } +} + +/** + * Reallocate space for bound columns. + * @param s statement pointer + * @param ncols number of columns + * @result ODBC error code + */ + +static SQLRETURN +mkbindcols(STMT *s, int ncols) +{ + if (s->bindcols) { + if (s->nbindcols < ncols) { + int i; + BINDCOL *bindcols = + xrealloc(s->bindcols, ncols * sizeof (BINDCOL)); + + if (!bindcols) { + return nomem(s); + } + for (i = s->nbindcols; i < ncols; i++) { + bindcols[i].type = SQL_UNKNOWN_TYPE; + bindcols[i].max = 0; + bindcols[i].lenp = NULL; + bindcols[i].valp = NULL; + bindcols[i].index = i; + bindcols[i].offs = 0; + } + s->bindcols = bindcols; + s->nbindcols = ncols; + } + } else if (ncols > 0) { + s->bindcols = (BINDCOL *) xmalloc(ncols * sizeof (BINDCOL)); + if (!s->bindcols) { + return nomem(s); + } + s->nbindcols = ncols; + unbindcols(s); + } + return SQL_SUCCESS; +} + +/** + * Internal function to retrieve row data, used by SQLFetch() and + * friends and SQLGetData(). + * @param s statement pointer + * @param col column number, 0 based + * @param otype output data type + * @param val output buffer + * @param len length of output buffer + * @param lenp output length + * @param partial flag for partial data retrieval + * @result ODBC error code + */ + +static SQLRETURN +getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, + SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, int partial) +{ + char **data, valdummy[16]; + SQLLEN dummy; + SQLINTEGER *ilenp = NULL; + int valnull = 0; + int type = otype; + SQLRETURN sret = SQL_NO_DATA; + + if (!lenp) { + lenp = &dummy; + } + /* workaround for JDK 1.7.0 on x86_64 */ + if (((SQLINTEGER *) lenp) + 1 == (SQLINTEGER *) val) { + ilenp = (SQLINTEGER *) lenp; + lenp = &dummy; + } + if (col >= s->ncols) { + setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); + return SQL_ERROR; + } + if (s->retr_data != SQL_RD_ON) { + return SQL_SUCCESS; + } + if (!s->rows) { + *lenp = SQL_NULL_DATA; + goto done; + } + if (s->rowp < 0 || s->rowp >= s->nrows) { + *lenp = SQL_NULL_DATA; + goto done; + } + type = mapdeftype(type, s->cols[col].type, s->cols[col].nosign ? 1 : 0, + s->nowchar[0]); +#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) + /* MS Access hack part 3 (map SQL_C_DEFAULT to SQL_C_CHAR) */ + if (type == SQL_C_WCHAR && otype == SQL_C_DEFAULT) { + type = SQL_C_CHAR; + } +#endif + data = s->rows + s->ncols + (s->rowp * s->ncols) + col; + if (!val) { + valnull = 1; + val = (SQLPOINTER) valdummy; + } + if (*data == NULL) { + *lenp = SQL_NULL_DATA; + switch (type) { + case SQL_C_UTINYINT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: +#ifdef SQL_BIT + case SQL_C_BIT: +#endif + *((SQLCHAR *) val) = 0; + break; + case SQL_C_USHORT: + case SQL_C_SHORT: + case SQL_C_SSHORT: + *((SQLSMALLINT *) val) = 0; + break; + case SQL_C_ULONG: + case SQL_C_LONG: + case SQL_C_SLONG: + *((SQLINTEGER *) val) = 0; + break; +#ifdef SQL_BIGINT + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + *((SQLBIGINT *) val) = 0; + break; +#endif + case SQL_C_FLOAT: + *((float *) val) = 0; + break; + case SQL_C_DOUBLE: + *((double *) val) = 0; + break; + case SQL_C_BINARY: + case SQL_C_CHAR: + *((SQLCHAR *) val) = '\0'; + break; +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: + *((SQLWCHAR *) val) = '\0'; + break; +#endif +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + memset((DATE_STRUCT *) val, 0, sizeof (DATE_STRUCT)); + break; +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + memset((TIME_STRUCT *) val, 0, sizeof (TIME_STRUCT)); + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + memset((TIMESTAMP_STRUCT *) val, 0, sizeof (TIMESTAMP_STRUCT)); + break; + default: + return SQL_ERROR; + } + } else { + char *endp = NULL; +#if defined(_WIN32) || defined(_WIN64) +#ifdef SQL_BIGINT + char endc; +#endif +#endif + + switch (type) { + case SQL_C_UTINYINT: + case SQL_C_TINYINT: + case SQL_C_STINYINT: + *((SQLCHAR *) val) = strtol(*data, &endp, 0); + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLCHAR); + } + break; +#ifdef SQL_BIT + case SQL_C_BIT: + *((SQLCHAR *) val) = getbool(*data); + *lenp = sizeof (SQLCHAR); + break; +#endif + case SQL_C_USHORT: + case SQL_C_SHORT: + case SQL_C_SSHORT: + *((SQLSMALLINT *) val) = strtol(*data, &endp, 0); + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLSMALLINT); + } + break; + case SQL_C_ULONG: + case SQL_C_LONG: + case SQL_C_SLONG: + *((SQLINTEGER *) val) = strtol(*data, &endp, 0); + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLINTEGER); + } + break; +#ifdef SQL_BIGINT + case SQL_C_UBIGINT: +#if defined(_WIN32) || defined(_WIN64) + if (sscanf(*data, "%I64u%c", (SQLUBIGINT *) val, &endc) != 1) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLUBIGINT); + } +#else +#ifdef __osf__ + *((SQLUBIGINT *) val) = strtoul(*data, &endp, 0); +#else + *((SQLUBIGINT *) val) = strtoull(*data, &endp, 0); +#endif + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLUBIGINT); + } +#endif + break; + case SQL_C_SBIGINT: +#if defined(_WIN32) || defined(_WIN64) + if (sscanf(*data, "%I64d%c", (SQLBIGINT *) val, &endc) != 1) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLBIGINT); + } +#else +#ifdef __osf__ + *((SQLBIGINT *) val) = strtol(*data, &endp, 0); +#else + *((SQLBIGINT *) val) = strtoll(*data, &endp, 0); +#endif + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (SQLBIGINT); + } +#endif + break; +#endif + case SQL_C_FLOAT: + *((float *) val) = ln_strtod(*data, &endp); + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (float); + } + break; + case SQL_C_DOUBLE: + *((double *) val) = ln_strtod(*data, &endp); + if (endp && endp == *data) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (double); + } + break; + case SQL_C_BINARY: { + int dlen, offs = 0; + char *bin; + + if (valnull) { + freep(&s->bincache); + s->binlen = 0; + goto doCHAR; + } + if (*data == s->bincell) { + if (s->bincache) { + bin = s->bincache; + dlen = s->binlen; + } else { + goto doCHAR; + } + } else { + char *dp; + int i; + + freep(&s->bincache); + dp = *data; + dlen = strlen(dp); + s->bincell = dp; + s->binlen = 0; + if (!(dp[0] == 'x' || dp[0] == 'X') || dp[1] != '\'' || + dp[dlen - 1] != '\'') { + goto doCHAR; + } + dlen -= 2; + dp += 2; + dlen = dlen / 2; + s->bincache = bin = xmalloc(dlen); + if (!bin) { + return nomem(s); + } + s->binlen = dlen; + memset(s->bincache, 0, dlen); + for (i = 0; i < dlen; i++) { + char *x; + int v; + + if (!*dp || !(x = strchr(xdigits, *dp))) { + goto converr; + } + v = x - xdigits; + bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4); + ++dp; + if (!*dp || !(x = strchr(xdigits, *dp))) { +converr: + freep(&s->bincache); + s->binlen = 0; + setstat(s, -1, "conversion error", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + v = x - xdigits; + bin[i] |= (v >= 16) ? (v - 6) : v; + ++dp; + } + bin = s->bincache; + } + if (partial && len && s->bindcols) { + if (s->bindcols[col].offs >= dlen) { + *lenp = 0; + if (!dlen && s->bindcols[col].offs == dlen) { + s->bindcols[col].offs = 1; + sret = SQL_SUCCESS; + goto done; + } + s->bindcols[col].offs = 0; + sret = SQL_NO_DATA; + goto done; + } + offs = s->bindcols[col].offs; + dlen -= offs; + } + if (val && len) { + memcpy(val, bin + offs, min(len, dlen)); + } + if (len < 1) { + *lenp = dlen; + } else { + *lenp = min(len, dlen); + if (*lenp == len && *lenp != dlen) { + *lenp = SQL_NO_TOTAL; + } + } + if (partial && len && s->bindcols) { + if (*lenp == SQL_NO_TOTAL) { + *lenp = dlen; + s->bindcols[col].offs += len; + setstat(s, -1, "data right truncated", "01004"); + if (s->bindcols[col].lenp) { + *s->bindcols[col].lenp = dlen; + } + sret = SQL_SUCCESS_WITH_INFO; + goto done; + } + s->bindcols[col].offs += *lenp; + } + if (*lenp == SQL_NO_TOTAL) { + *lenp = dlen; + setstat(s, -1, "data right truncated", "01004"); + sret = SQL_SUCCESS_WITH_INFO; + goto done; + } + break; + } + doCHAR: +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: +#endif + case SQL_C_CHAR: { + int doz, zlen = len - 1; + int dlen = strlen(*data); + int offs = 0; +#ifdef WCHARSUPPORT + SQLWCHAR *ucdata = NULL; + SQLCHAR *cdata = (SQLCHAR *) *data; +#endif + +#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE) + /* MS Access hack part 2 (reserved error -7748) */ + if (!valnull && + (s->cols == statSpec2P || s->cols == statSpec3P) && + type == SQL_C_WCHAR) { + if (len > 0 && len <= sizeof (SQLWCHAR)) { + ((char *) val)[0] = data[0][0]; + memset((char *) val + 1, 0, len - 1); + *lenp = 1; + sret = SQL_SUCCESS; + goto done; + } + } +#endif + +#ifdef WCHARSUPPORT + switch (type) { + case SQL_C_CHAR: + doz = 1; + break; + case SQL_C_WCHAR: + doz = sizeof (SQLWCHAR); + break; + default: + doz = 0; + break; + } + if (type == SQL_C_WCHAR) { + ucdata = uc_from_utf(cdata, dlen); + if (!ucdata) { + return nomem(s); + } + dlen = uc_strlen(ucdata) * sizeof (SQLWCHAR); + } +#if defined(_WIN32) || defined(_WIN64) + else if (*s->oemcp && type == SQL_C_CHAR) { + ucdata = (SQLWCHAR *) utf_to_wmb((char *) cdata, dlen); + if (!ucdata) { + return nomem(s); + } + cdata = (SQLCHAR *) ucdata; + dlen = strlen((char *) cdata); + } +#endif +#else + doz = (type == SQL_C_CHAR) ? 1 : 0; +#endif + if (partial && len && s->bindcols) { + if (s->bindcols[col].offs >= dlen) { +#ifdef WCHARSUPPORT + uc_free(ucdata); +#endif + *lenp = 0; + if (doz && val) { +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + ((SQLWCHAR *) val)[0] = 0; + } else { + ((char *) val)[0] = '\0'; + } +#else + ((char *) val)[0] = '\0'; +#endif + } + if (!dlen && s->bindcols[col].offs == dlen) { + s->bindcols[col].offs = 1; + sret = SQL_SUCCESS; + goto done; + } + s->bindcols[col].offs = 0; + sret = SQL_NO_DATA; + goto done; + } + offs = s->bindcols[col].offs; + dlen -= offs; + } + if (val && !valnull && len) { +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + uc_strncpy(val, ucdata + offs / sizeof (SQLWCHAR), + (len - doz) / sizeof (SQLWCHAR)); + } else { + strncpy(val, (char *) cdata + offs, len - doz); + } +#else + strncpy(val, *data + offs, len - doz); +#endif + } + if (valnull || len < 1) { + *lenp = dlen; + } else { + *lenp = min(len - doz, dlen); + if (*lenp == len - doz && *lenp != dlen) { + *lenp = SQL_NO_TOTAL; + } else if (*lenp < zlen) { + zlen = *lenp; + } + } + if (len && !valnull && doz) { +#ifdef WCHARSUPPORT + if (type == SQL_C_WCHAR) { + ((SQLWCHAR *) val)[zlen / sizeof (SQLWCHAR)] = 0; + } else { + ((char *) val)[zlen] = '\0'; + } +#else + ((char *) val)[zlen] = '\0'; +#endif + } +#ifdef WCHARSUPPORT + uc_free(ucdata); +#endif + if (partial && len && s->bindcols) { + if (*lenp == SQL_NO_TOTAL) { + *lenp = dlen; + s->bindcols[col].offs += len - doz; + setstat(s, -1, "data right truncated", "01004"); + if (s->bindcols[col].lenp) { + *s->bindcols[col].lenp = dlen; + } + sret = SQL_SUCCESS_WITH_INFO; + goto done; + } + s->bindcols[col].offs += *lenp; + } + if (*lenp == SQL_NO_TOTAL) { + *lenp = dlen; + setstat(s, -1, "data right truncated", "01004"); + sret = SQL_SUCCESS_WITH_INFO; + goto done; + } + break; + } +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: +#endif + case SQL_C_DATE: + if (str2date(*data, (DATE_STRUCT *) val) < 0) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (DATE_STRUCT); + } + break; +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: +#endif + case SQL_C_TIME: + if (str2time(*data, (TIME_STRUCT *) val) < 0) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (TIME_STRUCT); + } + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: +#endif + case SQL_C_TIMESTAMP: + if (str2timestamp(*data, (TIMESTAMP_STRUCT *) val) < 0) { + *lenp = SQL_NULL_DATA; + } else { + *lenp = sizeof (TIMESTAMP_STRUCT); + } + switch (s->cols[col].prec) { + case 0: + ((TIMESTAMP_STRUCT *) val)->fraction = 0; + break; + case 1: + ((TIMESTAMP_STRUCT *) val)->fraction /= 100000000; + ((TIMESTAMP_STRUCT *) val)->fraction *= 100000000; + break; + case 2: + ((TIMESTAMP_STRUCT *) val)->fraction /= 10000000; + ((TIMESTAMP_STRUCT *) val)->fraction *= 10000000; + break; + } + break; + default: + return SQL_ERROR; + } + } + sret = SQL_SUCCESS; +done: + if (ilenp) { + *ilenp = *lenp; + } + return sret; +} + +/** + * Interal bind C variable to column of result set. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param type output type + * @param val output buffer + * @param max length of output buffer + * @param lenp output length pointer + * @result ODBC error code + */ + +static SQLRETURN +drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, + SQLPOINTER val, SQLLEN max, SQLLEN *lenp) +{ + STMT *s; + int sz = 0; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (col < 1) { + if (col == 0 && s->bkmrk == SQL_UB_ON && + type == SQL_C_BOOKMARK) { + s->bkmrkcol.type = val ? type : SQL_UNKNOWN_TYPE; + s->bkmrkcol.max = val ? sizeof (SQLINTEGER) : 0; + s->bkmrkcol.lenp = val ? lenp : 0; + s->bkmrkcol.valp = val; + s->bkmrkcol.offs = 0; + if (val && lenp) { + *lenp = 0; + } + return SQL_SUCCESS; + } else if (col == 0 && s->bkmrk == SQL_UB_VARIABLE && + type == SQL_C_VARBOOKMARK && + max >= sizeof (sqlite4_int64)) { + s->bkmrkcol.type = val ? type : SQL_UNKNOWN_TYPE; + s->bkmrkcol.max = val ? max : 0; + s->bkmrkcol.lenp = val ? lenp : 0; + s->bkmrkcol.valp = val; + s->bkmrkcol.offs = 0; + if (val && lenp) { + *lenp = 0; + } + return SQL_SUCCESS; + } + setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); + return SQL_ERROR; + } + if (mkbindcols(s, col) != SQL_SUCCESS) { + return SQL_ERROR; + } + --col; + if (type == SQL_C_DEFAULT) { + type = mapdeftype(type, s->cols[col].type, 0, + s->nowchar[0] || s->nowchar[1]); + } + switch (type) { + case SQL_C_LONG: + case SQL_C_ULONG: + case SQL_C_SLONG: + sz = sizeof (SQLINTEGER); + break; + case SQL_C_TINYINT: + case SQL_C_UTINYINT: + case SQL_C_STINYINT: + sz = sizeof (SQLCHAR); + break; + case SQL_C_SHORT: + case SQL_C_USHORT: + case SQL_C_SSHORT: + sz = sizeof (SQLSMALLINT); + break; + case SQL_C_FLOAT: + sz = sizeof (SQLFLOAT); + break; + case SQL_C_DOUBLE: + sz = sizeof (SQLDOUBLE); + break; + case SQL_C_TIMESTAMP: + sz = sizeof (SQL_TIMESTAMP_STRUCT); + break; + case SQL_C_TIME: + sz = sizeof (SQL_TIME_STRUCT); + break; + case SQL_C_DATE: + sz = sizeof (SQL_DATE_STRUCT); + break; + case SQL_C_CHAR: + break; +#ifdef WCHARSUPPORT + case SQL_C_WCHAR: + break; +#endif +#ifdef SQL_C_TYPE_DATE + case SQL_C_TYPE_DATE: + sz = sizeof (SQL_DATE_STRUCT); + break; +#endif +#ifdef SQL_C_TYPE_TIME + case SQL_C_TYPE_TIME: + sz = sizeof (SQL_TIME_STRUCT); + break; +#endif +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_C_TYPE_TIMESTAMP: + sz = sizeof (SQL_TIMESTAMP_STRUCT); + break; +#endif +#ifdef SQL_BIT + case SQL_C_BIT: + sz = sizeof (SQLCHAR); + break; +#endif + case SQL_C_BINARY: + break; +#ifdef SQL_BIGINT + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + sz = sizeof (SQLBIGINT); + break; +#endif + default: + if (val == NULL) { + /* fall through, unbinding column */ + break; + } + setstat(s, -1, "invalid type %d", "HY003", type); + return SQL_ERROR; + } + if (val == NULL) { + /* unbind column */ + s->bindcols[col].type = SQL_UNKNOWN_TYPE; + s->bindcols[col].max = 0; + s->bindcols[col].lenp = NULL; + s->bindcols[col].valp = NULL; + s->bindcols[col].offs = 0; + } else { + if (sz == 0 && max < 0) { + setstat(s, -1, "invalid length", "HY090"); + return SQL_ERROR; + } + s->bindcols[col].type = type; + s->bindcols[col].max = (sz == 0) ? max : sz; + s->bindcols[col].lenp = lenp; + s->bindcols[col].valp = val; + s->bindcols[col].offs = 0; + if (lenp) { + *lenp = 0; + } + } + return SQL_SUCCESS; +} + +/** + * Bind C variable to column of result set. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param type output type + * @param val output buffer + * @param max length of output buffer + * @param lenp output length pointer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, + SQLPOINTER val, SQLLEN max, SQLLEN *lenp) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvbindcol(stmt, col, type, val, max, lenp); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Columns for result set of SQLTables(). + */ + +static COL tableSpec2[] = { + { "SYSTEM", "COLUMN", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "TABLE_TYPE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 } +}; + +static COL tableSpec3[] = { + { "SYSTEM", "COLUMN", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "TABLE_TYPE", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 } +}; + +/** + * Retrieve information on tables and/or views. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param type types of tables string or NULL + * @param typeLen length of types of tables string or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvtables(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLCHAR *type, SQLSMALLINT typeLen) +{ + SQLRETURN ret; + STMT *s; + DBC *d; + int ncols, asize, rc, size, npatt; + char *errp = NULL, *sql, tname[512]; + char *where = "(type = 'table' or type = 'view')"; + + ret = mkresultset(stmt, tableSpec2, array_size(tableSpec2), + tableSpec3, array_size(tableSpec3), &asize); + if (ret != SQL_SUCCESS) { + return ret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] == '%') { + int size = 3 * asize; + + s->rows = xmalloc(size * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + return nomem(s); + } + memset(s->rows, 0, sizeof (char *) * size); + s->ncols = asize; + s->rows[s->ncols + 0] = ""; + s->rows[s->ncols + 1] = ""; + s->rows[s->ncols + 2] = ""; + s->rows[s->ncols + 3] = "TABLE"; + s->rows[s->ncols + 5] = ""; + s->rows[s->ncols + 6] = ""; + s->rows[s->ncols + 7] = ""; + s->rows[s->ncols + 8] = "VIEW"; +#ifdef MEMORY_DEBUG + s->rowfree = xfree__; +#else + s->rowfree = sqlite4_free; +#endif + s->nrows = 2; + s->rowp = s->rowprs = -1; + return SQL_SUCCESS; + } + if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') { + table = NULL; + goto doit; + } + if (schema && (schemaLen > 0 || schemaLen == SQL_NTS) && + schema[0] == '%') { + if ((!cat || catLen == 0 || !cat[0]) && + (!table || tableLen == 0 || !table[0])) { + table = NULL; + goto doit; + } + } + if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] != '\0') { + char tmp[256], *t; + int with_view = 0, with_table = 0; + + if (typeLen == SQL_NTS) { + strncpy(tmp, (char *) type, sizeof (tmp)); + tmp[sizeof (tmp) - 1] = '\0'; + } else { + int len = min(sizeof (tmp) - 1, typeLen); + + strncpy(tmp, (char *) type, len); + tmp[len] = '\0'; + } + t = tmp; + while (*t) { + *t = TOLOWER(*t); + t++; + } + t = tmp; + unescpat(t); + while (t) { + if (t[0] == '\'') { + ++t; + } + if (strncmp(t, "table", 5) == 0) { + with_table++; + } else if (strncmp(t, "view", 4) == 0) { + with_view++; + } + t = strchr(t, ','); + if (t) { + ++t; + } + } + if (with_view && with_table) { + /* where is already preset */ + } else if (with_view && !with_table) { + where = "type = 'view'"; + } else if (!with_view && with_table) { + where = "type = 'table'"; + } else { + return SQL_SUCCESS; + } + } +doit: + if (!table) { + size = 1; + tname[0] = '%'; + } else { + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + } + tname[size] = '\0'; + npatt = unescpat(tname); +#if defined(_WIN32) || defined(_WIN64) + sql = sqlite4_mprintf(0, "select %s as 'TABLE_QUALIFIER', " + "%s as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "upper(type) as 'TABLE_TYPE', " + "NULL as 'REMARKS' " + "from sqlite_master where %s " + "and tbl_name %s %Q", + d->xcelqrx ? "''" : "NULL", + d->xcelqrx ? "'main'" : "NULL", + where, + npatt ? "like" : "=", tname); +#else + sql = sqlite4_mprintf(0, "select NULL as 'TABLE_QUALIFIER', " + "NULL as 'TABLE_OWNER', " + "tbl_name as 'TABLE_NAME', " + "upper(type) as 'TABLE_TYPE', " + "NULL as 'REMARKS' " + "from sqlite_master where %s " + "and tbl_name %s %Q", where, + npatt ? "like" : "=", tname); +#endif + if (!sql) { + return nomem(s); + } + ret = starttran(s); + if (ret != SQL_SUCCESS) { + sqlite4_free(0, sql); + return ret; + } + dbtraceapi(d, "sqlite4_get_table", sql); + rc = sqlite4_get_table(d->sqlite, sql, &s->rows, &s->nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (rc == SQLITE4_OK) { + if (ncols != s->ncols) { + freeresult(s, 0); + s->nrows = 0; + } else { + s->rowfree = freerows; + } + } else { + s->nrows = 0; + s->rows = NULL; + s->rowfree = NULL; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + s->rowp = s->rowprs = -1; + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve information on tables and/or views. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param type types of tables string or NULL + * @param typeLen length of types of tables string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLTables(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLCHAR *type, SQLSMALLINT typeLen) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL, *y = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvtables(stmt, cat, catLen, schema, schemaLen, + table, tableLen, type, typeLen); + goto done2; + } + if (cat) { + c = wmb_to_utf_c((char *) cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (type) { + y = wmb_to_utf_c((char *) type, typeLen); + if (!y) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvtables(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, (SQLCHAR *) y, SQL_NTS); +#else + ret = drvtables(stmt, cat, catLen, schema, schemaLen, + table, tableLen, type, typeLen); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(y); + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve information on tables and/or views. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param type types of tables string or NULL + * @param typeLen length of types of tables string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLTablesW(SQLHSTMT stmt, + SQLWCHAR *cat, SQLSMALLINT catLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen, + SQLWCHAR *type, SQLSMALLINT typeLen) +{ + char *c = NULL, *s = NULL, *t = NULL, *y = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cat) { + c = uc_to_utf_c(cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (type) { + y = uc_to_utf_c(type, typeLen); + if (!y) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvtables(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, (SQLCHAR *) y, SQL_NTS); +done: + uc_free(y); + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLColumns(). + */ + +static COL colSpec2[] = { + { "SYSTEM", "COLUMN", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "PRECISION", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "LENGTH", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "SCALE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "RADIX", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "COLUMN_DEF", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "SQL_DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "SQL_DATETIME_SUB", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "ORDINAL_POSITION", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "IS_NULLABLE", SCOL_VARCHAR, 50 } +}; + +static COL colSpec3[] = { + { "SYSTEM", "COLUMN", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "COLUMN_SIZE", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "BUFFER_LENGTH", SQL_INTEGER, 50 }, + { "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "NUM_PREC_RADIX", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "COLUMN_DEF", SCOL_VARCHAR, 50 }, + { "SYSTEM", "COLUMN", "SQL_DATA_TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "SQL_DATETIME_SUB", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "ORDINAL_POSITION", SQL_SMALLINT, 50 }, + { "SYSTEM", "COLUMN", "IS_NULLABLE", SCOL_VARCHAR, 50 } +}; + +/** + * Internal retrieve column information on table. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param col column name/pattern or NULL + * @param colLen length of column name/pattern or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvcolumns(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLCHAR *col, SQLSMALLINT colLen) +{ + SQLRETURN sret; + STMT *s; + DBC *d; + int ret, nrows, ncols, asize, i, k, roffs, namec; + int tnrows, tncols, npatt; + PTRDIFF_T size; + char *errp = NULL, *sql, tname[512], cname[512], **rowp, **trows; + + sret = mkresultset(stmt, colSpec2, array_size(colSpec2), + colSpec3, array_size(colSpec3), &asize); + if (sret != SQL_SUCCESS) { + return sret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (!table) { + size = 1; + tname[0] = '%'; + } else { + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + } + tname[size] = '\0'; + npatt = unescpat(tname); + size = 0; + if (col) { + if (colLen == SQL_NTS) { + size = sizeof (cname) - 1; + } else { + size = min(sizeof (cname) - 1, colLen); + } + strncpy(cname, (char *) col, size); + } + cname[size] = '\0'; + if (!strcmp(cname, "%")) { + cname[0] = '\0'; + } + sql = sqlite4_mprintf(0, "select tbl_name from sqlite_master where " + "(type = 'table' or type = 'view') " + "and tbl_name %s %Q", npatt ? "like" : "=", tname); + if (!sql) { + return nomem(s); + } + sret = starttran(s); + if (sret != SQL_SUCCESS) { + sqlite4_free(0, sql); + return sret; + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &trows, &tnrows, &tncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + /* pass 1: compute number of rows of result set */ + if (tncols * tnrows <= 0) { + freerows(trows); + return SQL_SUCCESS; + } + size = 0; + for (i = 1; i <= tnrows; i++) { + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", trows[i]); + if (!sql) { + freerows(trows); + return nomem(s); + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + freerows(trows); + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + if (ncols * nrows > 0) { + namec = -1; + for (k = 0; k < ncols; k++) { + if (strcmp(rowp[k], "name") == 0) { + namec = k; + break; + } + } + if (cname[0]) { + if (namec >= 0) { + for (k = 1; k <= nrows; k++) { + if (namematch(rowp[k * ncols + namec], cname, 1)) { + size++; + } + } + } + } else { + size += nrows; + } + } + freerows(rowp); + } + /* pass 2: fill result set */ + if (size <= 0) { + freerows(trows); + return SQL_SUCCESS; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + freerows(trows); + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + roffs = 1; + for (i = 1; i <= tnrows; i++) { + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", trows[i]); + if (!sql) { + freerows(trows); + return nomem(s); + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + freerows(trows); + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + if (ncols * nrows > 0) { + int m, mr, nr = nrows; + + namec = -1; + for (k = 0; k < ncols; k++) { + if (strcmp(rowp[k], "name") == 0) { + namec = k; + break; + } + } + if (cname[0]) { + nr = 0; + if (namec >= 0) { + for (k = 1; k <= nrows; k++) { + if (namematch(rowp[k * ncols + namec], cname, 1)) { + nr++; + } + } + } + } + for (k = 0; k < nr; k++) { + m = asize * (roffs + k); + s->rows[m + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[m + 1] = xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[m + 1] = xstrdup(""); +#endif + s->rows[m + 2] = xstrdup(trows[i]); + s->rows[m + 8] = xstrdup("10"); + s->rows[m + 9] = xstrdup("0"); + s->rows[m + 15] = xstrdup("16384"); + } + for (k = 0; nr && k < ncols; k++) { + if (strcmp(rowp[k], "cid") == 0) { + for (mr = 0, m = 1; m <= nrows; m++) { + char buf[256]; + int ir, coln = k; + + if (cname[0] && + !namematch(rowp[m * ncols + namec], cname, 1)) { + continue; + } + ir = asize * (roffs + mr); + sscanf(rowp[m * ncols + k], "%d", &coln); + sprintf(buf, "%d", coln + 1); + s->rows[ir + 16] = xstrdup(buf); + ++mr; + } + } else if (k == namec) { + for (mr = 0, m = 1; m <= nrows; m++) { + int ir; + + if (cname[0] && + !namematch(rowp[m * ncols + namec], cname, 1)) { + continue; + } + ir = asize * (roffs + mr); + s->rows[ir + 3] = xstrdup(rowp[m * ncols + k]); + ++mr; + } + } else if (strcmp(rowp[k], "notnull") == 0) { + for (mr = 0, m = 1; m <= nrows; m++) { + int ir; + + if (cname[0] && + !namematch(rowp[m * ncols + namec], cname, 1)) { + continue; + } + ir = asize * (roffs + mr); + if (*rowp[m * ncols + k] != '0') { + s->rows[ir + 10] = xstrdup(stringify(SQL_FALSE)); + } else { + s->rows[ir + 10] = xstrdup(stringify(SQL_TRUE)); + } + s->rows[ir + 17] = + xstrdup((*rowp[m * ncols + k] != '0') ? + "NO" : "YES"); + ++mr; + } + } else if (strcmp(rowp[k], "dflt_value") == 0) { + for (mr = 0, m = 1; m <= nrows; m++) { + char *dflt = unquote(rowp[m * ncols + k]); + int ir; + + if (cname[0] && + !namematch(rowp[m * ncols + namec], cname, 1)) { + continue; + } + ir = asize * (roffs + mr); + s->rows[ir + 12] = xstrdup(dflt ? dflt : "NULL"); + ++mr; + } + } else if (strcmp(rowp[k], "type") == 0) { + for (mr = 0, m = 1; m <= nrows; m++) { + char *typename = rowp[m * ncols + k]; + int sqltype, mm, dd, ir; + char buf[256]; + + if (cname[0] && + !namematch(rowp[m * ncols + namec], cname, 1)) { + continue; + } + ir = asize * (roffs + mr); + s->rows[ir + 5] = xstrdup(typename); + sqltype = mapsqltype(typename, NULL, *s->ov3, + s->nowchar[0], s->dobigint); + getmd(typename, sqltype, &mm, &dd); +#ifdef SQL_LONGVARCHAR + if (sqltype == SQL_VARCHAR && mm > 255) { + sqltype = SQL_LONGVARCHAR; + } +#endif +#ifdef WINTERFACE +#ifdef SQL_WLONGVARCHAR + if (sqltype == SQL_WVARCHAR && mm > 255) { + sqltype = SQL_WLONGVARCHAR; + } +#endif +#endif + if (sqltype == SQL_VARBINARY && mm > 255) { + sqltype = SQL_LONGVARBINARY; + } + sprintf(buf, "%d", sqltype); + s->rows[ir + 4] = xstrdup(buf); + s->rows[ir + 13] = xstrdup(buf); + sprintf(buf, "%d", mm); + s->rows[ir + 7] = xstrdup(buf); + sprintf(buf, "%d", dd); + s->rows[ir + 6] = xstrdup(buf); + ++mr; + } + } + } + roffs += nr; + } + freerows(rowp); + } + freerows(trows); + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve column information on table. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param col column name/pattern or NULL + * @param colLen length of column name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColumns(SQLHSTMT stmt, + SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLCHAR *col, SQLSMALLINT colLen) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL, *k = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvcolumns(stmt, cat, catLen, schema, schemaLen, + table, tableLen, col, colLen); + goto done2; + } + if (cat) { + c = wmb_to_utf_c((char *) cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (col) { + k = wmb_to_utf_c((char *) col, colLen); + if (!k) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvcolumns(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, (SQLCHAR *) k, SQL_NTS); +#else + ret = drvcolumns(stmt, cat, catLen, schema, schemaLen, + table, tableLen, col, colLen); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(k); + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve column information on table (UNICODE version). + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param col column name/pattern or NULL + * @param colLen length of column name/pattern or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColumnsW(SQLHSTMT stmt, + SQLWCHAR *cat, SQLSMALLINT catLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen, + SQLWCHAR *col, SQLSMALLINT colLen) +{ + char *c = NULL, *s = NULL, *t = NULL, *k = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cat) { + c = uc_to_utf_c(cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (col) { + k = uc_to_utf_c(col, colLen); + if (!k) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvcolumns(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, (SQLCHAR *) k, SQL_NTS); +done: + uc_free(k); + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; + +} +#endif + +/** + * Columns for result set of SQLGetTypeInfo(). + */ + +static COL typeSpec2[] = { + { "SYSTEM", "TYPE", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "DATA_TYPE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "PRECISION", SQL_INTEGER, 9 }, + { "SYSTEM", "TYPE", "LITERAL_PREFIX", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "LITERAL_SUFFIX", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "CREATE_PARAMS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "NULLABLE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "CASE_SENSITIVE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "SEARCHABLE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "UNSIGNED_ATTRIBUTE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "MONEY", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "AUTO_INCREMENT", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "LOCAL_TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "MINIMUM_SCALE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "MAXIMUM_SCALE", SQL_SMALLINT, 2 } +}; + +static COL typeSpec3[] = { + { "SYSTEM", "TYPE", "TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "DATA_TYPE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "COLUMN_SIZE", SQL_INTEGER, 9 }, + { "SYSTEM", "TYPE", "LITERAL_PREFIX", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "LITERAL_SUFFIX", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "CREATE_PARAMS", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "NULLABLE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "CASE_SENSITIVE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "SEARCHABLE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "UNSIGNED_ATTRIBUTE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "FIXED_PREC_SCALE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "AUTO_UNIQUE_VALUE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "LOCAL_TYPE_NAME", SCOL_VARCHAR, 50 }, + { "SYSTEM", "TYPE", "MINIMUM_SCALE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "MAXIMUM_SCALE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "SQL_DATA_TYPE", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "SQL_DATETIME_SUB", SQL_SMALLINT, 2 }, + { "SYSTEM", "TYPE", "NUM_PREC_RADIX", SQL_INTEGER, 4 }, + { "SYSTEM", "TYPE", "INTERVAL_PRECISION", SQL_SMALLINT, 2 } +}; + +/** + * Internal function to build up data type information as row in result set. + * @param s statement pointer + * @param row row number + * @param asize number of items in a row + * @param typename name of type + * @param type integer SQL type + * @param tind type index + */ + +static void +mktypeinfo(STMT *s, int row, int asize, char *typename, int type, int tind) +{ + int offs = row * asize; + char *tcode, *crpar = NULL, *quote = NULL, *sign = stringify(SQL_FALSE); + static char tcodes[32 * 32]; + + if (tind <= 0) { + tind = row; + } + tcode = tcodes + tind * 32; + sprintf(tcode, "%d", type); + s->rows[offs + 0] = typename; + s->rows[offs + 1] = tcode; + if (asize >= 17) { + s->rows[offs + 15] = tcode; + s->rows[offs + 16] = "0"; + } + switch (type) { + default: +#ifdef SQL_LONGVARCHAR + case SQL_LONGVARCHAR: +#ifdef WINTERFACE + case SQL_WLONGVARCHAR: +#endif + crpar = "length"; + quote = "'"; + sign = NULL; + s->rows[offs + 2] = "65536"; + break; +#endif +#ifdef SQL_BIT + case SQL_BIT: + sign = NULL; + s->rows[offs + 2] = "1"; + break; +#endif + case SQL_CHAR: + case SQL_VARCHAR: +#ifdef WINTERFACE + case SQL_WCHAR: + case SQL_WVARCHAR: +#endif + s->rows[offs + 2] = "255"; + crpar = "length"; + quote = "'"; + sign = NULL; + break; + case SQL_TINYINT: + s->rows[offs + 2] = "3"; + break; + case SQL_SMALLINT: + s->rows[offs + 2] = "5"; + break; + case SQL_INTEGER: + s->rows[offs + 2] = "9"; + break; +#ifdef SQL_BIGINT + case SQL_BIGINT: + s->rows[offs + 2] = "19"; + break; +#endif + case SQL_FLOAT: + s->rows[offs + 2] = "7"; + break; + case SQL_DOUBLE: + s->rows[offs + 2] = "15"; + break; +#ifdef SQL_TYPE_DATE + case SQL_TYPE_DATE: +#endif + case SQL_DATE: + s->rows[offs + 2] = "10"; + quote = "'"; + sign = NULL; + break; +#ifdef SQL_TYPE_TIME + case SQL_TYPE_TIME: +#endif + case SQL_TIME: + s->rows[offs + 2] = "8"; + quote = "'"; + sign = NULL; + break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + case SQL_TIMESTAMP: + s->rows[offs + 2] = "32"; + quote = "'"; + sign = NULL; + break; + case SQL_VARBINARY: + sign = NULL; + s->rows[offs + 2] = "255"; + break; + case SQL_LONGVARBINARY: + sign = NULL; + s->rows[offs + 2] = "65536"; + break; + } + s->rows[offs + 3] = s->rows[offs + 4] = quote; + s->rows[offs + 5] = crpar; + s->rows[offs + 6] = stringify(SQL_NULLABLE); + s->rows[offs + 7] = stringify(SQL_FALSE); + s->rows[offs + 8] = stringify(SQL_SEARCHABLE); + s->rows[offs + 9] = sign; + s->rows[offs + 10] = stringify(SQL_FALSE); + s->rows[offs + 11] = stringify(SQL_FALSE); + s->rows[offs + 12] = typename; + switch (type) { + case SQL_DATE: + case SQL_TIME: + s->rows[offs + 13] = "0"; + s->rows[offs + 14] = "0"; + break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + case SQL_TIMESTAMP: + s->rows[offs + 13] = "0"; + s->rows[offs + 14] = "3"; + break; + default: + s->rows[offs + 13] = NULL; + s->rows[offs + 14] = NULL; + break; + } +} + +/** + * Helper function to sort type information. + * Callback for qsort(). + * @param a first item to compare + * @param b second item to compare + * @result ==0, <0, >0 according to data type number + */ + +static int +typeinfosort(const void *a, const void *b) +{ + char **pa = (char **) a; + char **pb = (char **) b; + int na, nb; + + na = strtol(pa[1], NULL, 0); + nb = strtol(pb[1], NULL, 0); + return na - nb; +} + +/** + * Internal return data type information. + * @param stmt statement handle + * @param sqltype which type to retrieve + * @result ODBC error code + */ + +static SQLRETURN +drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype) +{ + SQLRETURN ret; + STMT *s; + int asize; + + ret = mkresultset(stmt, typeSpec2, array_size(typeSpec2), + typeSpec3, array_size(typeSpec3), &asize); + if (ret != SQL_SUCCESS) { + return ret; + } + s = (STMT *) stmt; +#ifdef SQL_LONGVARCHAR + s->nrows = (sqltype == SQL_ALL_TYPES) ? 13 : 1; +#else + s->nrows = (sqltype == SQL_ALL_TYPES) ? 12 : 1; +#endif + if (sqltype == SQL_ALL_TYPES) { +#ifdef WINTERFACE + s->nrows += 2; +#ifdef SQL_WLONGVARCHAR + s->nrows += 2; +#endif +#endif + } + if (sqltype == SQL_ALL_TYPES) { + s->nrows += 2; +#ifdef SQL_BIT + s->nrows += 1; +#endif +#ifdef SQL_BIGINT + s->nrows += 1; +#endif + } + s->rows = (char **) xmalloc(sizeof (char *) * (s->nrows + 1) * asize); + if (!s->rows) { + s->nrows = 0; + return nomem(s); + } +#ifdef MEMORY_DEBUG + s->rowfree = xfree__; +#else + s->rowfree = sqlite4_free; +#endif + memset(s->rows, 0, sizeof (char *) * (s->nrows + 1) * asize); + if (sqltype == SQL_ALL_TYPES) { + int cc = 1; + + mktypeinfo(s, cc++, asize, "varchar", SQL_VARCHAR, 0); + mktypeinfo(s, cc++, asize, "tinyint", SQL_TINYINT, 0); + mktypeinfo(s, cc++, asize, "smallint", SQL_SMALLINT, 0); + mktypeinfo(s, cc++, asize, "integer", SQL_INTEGER, 0); + mktypeinfo(s, cc++, asize, "float", SQL_FLOAT, 0); + mktypeinfo(s, cc++, asize, "double", SQL_DOUBLE, 0); +#ifdef SQL_TYPE_DATE + mktypeinfo(s, cc++, asize, "date", + (*s->ov3) ? SQL_TYPE_DATE : SQL_DATE, 0); +#else + mktypeinfo(s, cc++, asize, "date", SQL_DATE, 0); +#endif +#ifdef SQL_TYPE_TIME + mktypeinfo(s, cc++, asize, "time", + (*s->ov3) ? SQL_TYPE_TIME : SQL_TIME, 0); +#else + mktypeinfo(s, cc++, asize, "time", SQL_TIME, 0); +#endif +#ifdef SQL_TYPE_TIMESTAMP + mktypeinfo(s, cc++, asize, "timestamp", + (*s->ov3) ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP, 0); +#else + mktypeinfo(s, cc++, asize, "timestamp", SQL_TIMESTAMP, 0); +#endif + mktypeinfo(s, cc++, asize, "char", SQL_CHAR, 0); + mktypeinfo(s, cc++, asize, "numeric", SQL_DOUBLE, 0); +#ifdef SQL_LONGVARCHAR + mktypeinfo(s, cc++, asize, "text", SQL_LONGVARCHAR, 0); + mktypeinfo(s, cc++, asize, "longvarchar", SQL_LONGVARCHAR, 0); +#else + mktypeinfo(s, cc++, asize, "text", SQL_VARCHAR, 0); +#endif + mktypeinfo(s, cc++, asize, "varbinary", SQL_VARBINARY, 0); + mktypeinfo(s, cc++, asize, "longvarbinary", SQL_LONGVARBINARY, 0); +#ifdef SQL_BIT + mktypeinfo(s, cc++, asize, "bit", SQL_BIT, 0); +#endif +#ifdef SQL_BIGINT + mktypeinfo(s, cc++, asize, "bigint", SQL_BIGINT, 0); +#endif +#ifdef WINTERFACE + mktypeinfo(s, cc++, asize, "wvarchar", SQL_WVARCHAR, 0); + mktypeinfo(s, cc++, asize, "wchar", SQL_WCHAR, 0); +#ifdef SQL_WLONGVARCHAR + mktypeinfo(s, cc++, asize, "wtext", SQL_WLONGVARCHAR, 0); + mktypeinfo(s, cc++, asize, "longwvarchar", SQL_WLONGVARCHAR, 0); +#endif +#endif + qsort(s->rows + asize, s->nrows, sizeof (char *) * asize, + typeinfosort); + } else { + switch (sqltype) { + case SQL_CHAR: + mktypeinfo(s, 1, asize, "char", SQL_CHAR, 10); + break; + case SQL_VARCHAR: + mktypeinfo(s, 1, asize, "varchar", SQL_VARCHAR, 1); + break; + case SQL_TINYINT: + mktypeinfo(s, 1, asize, "tinyint", SQL_TINYINT, 2); + break; + case SQL_SMALLINT: + mktypeinfo(s, 1, asize, "smallint", SQL_SMALLINT, 3); + break; + case SQL_INTEGER: + mktypeinfo(s, 1, asize, "integer", SQL_INTEGER, 4); + break; + case SQL_FLOAT: + mktypeinfo(s, 1, asize, "float", SQL_FLOAT, 5); + break; + case SQL_DOUBLE: + mktypeinfo(s, 1, asize, "double", SQL_DOUBLE, 6); + break; +#ifdef SQL_TYPE_DATE + case SQL_TYPE_DATE: + mktypeinfo(s, 1, asize, "date", SQL_TYPE_DATE, 25); + break; +#endif + case SQL_DATE: + mktypeinfo(s, 1, asize, "date", SQL_DATE, 7); + break; +#ifdef SQL_TYPE_TIME + case SQL_TYPE_TIME: + mktypeinfo(s, 1, asize, "time", SQL_TYPE_TIME, 26); + break; +#endif + case SQL_TIME: + mktypeinfo(s, 1, asize, "time", SQL_TIME, 8); + break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: + mktypeinfo(s, 1, asize, "timestamp", SQL_TYPE_TIMESTAMP, 27); + break; +#endif + case SQL_TIMESTAMP: + mktypeinfo(s, 1, asize, "timestamp", SQL_TIMESTAMP, 9); + break; +#ifdef SQL_LONGVARCHAR + case SQL_LONGVARCHAR: + mktypeinfo(s, 1, asize, "longvarchar", SQL_LONGVARCHAR, 12); + break; +#endif + case SQL_VARBINARY: + mktypeinfo(s, 1, asize, "varbinary", SQL_VARBINARY, 30); + break; + case SQL_LONGVARBINARY: + mktypeinfo(s, 1, asize, "longvarbinary", SQL_LONGVARBINARY, 31); + break; +#ifdef SQL_BIT + case SQL_BIT: + mktypeinfo(s, 1, asize, "bit", SQL_BIT, 29); + break; +#endif +#ifdef SQL_BIGINT + case SQL_BIGINT: + mktypeinfo(s, 1, asize, "bigint", SQL_BIGINT, 28); + break; +#endif +#ifdef WINTERFACE +#ifdef SQL_WCHAR + case SQL_WCHAR: + mktypeinfo(s, 1, asize, "wchar", SQL_WCHAR, 18); + break; +#endif +#ifdef SQL_WVARCHAR + case SQL_WVARCHAR: + mktypeinfo(s, 1, asize, "wvarchar", SQL_WVARCHAR, 19); + break; +#endif +#ifdef SQL_WLONGVARCHAR + case SQL_WLONGVARCHAR: + mktypeinfo(s, 1, asize, "longwvarchar", SQL_WLONGVARCHAR, 20); + break; +#endif +#endif + default: + s->nrows = 0; + } + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Return data type information. + * @param stmt statement handle + * @param sqltype which type to retrieve + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetTypeInfo(SQLHSTMT stmt, SQLSMALLINT sqltype) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgettypeinfo(stmt, sqltype); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Return data type information (UNICODE version). + * @param stmt statement handle + * @param sqltype which type to retrieve + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetTypeInfoW(SQLHSTMT stmt, SQLSMALLINT sqltype) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvgettypeinfo(stmt, sqltype); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Columns for result set of SQLStatistics(). + */ + +static COL statSpec2[] = { + { "SYSTEM", "STATISTICS", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "STATISTICS", "TABLE_OWNER", SCOL_VARCHAR, 50 }, + { "SYSTEM", "STATISTICS", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "NON_UNIQUE", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "INDEX_QUALIFIER", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "INDEX_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "SEQ_IN_INDEX", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "COLLATION", SCOL_CHAR, 1 }, + { "SYSTEM", "STATISTICS", "CARDINALITY", SQL_INTEGER, 50 }, + { "SYSTEM", "STATISTICS", "PAGES", SQL_INTEGER, 50 }, + { "SYSTEM", "STATISTICS", "FILTER_CONDITION", SCOL_VARCHAR, 255 } +}; + +static COL statSpec3[] = { + { "SYSTEM", "STATISTICS", "TABLE_CAT", SCOL_VARCHAR, 50 }, + { "SYSTEM", "STATISTICS", "TABLE_SCHEM", SCOL_VARCHAR, 50 }, + { "SYSTEM", "STATISTICS", "TABLE_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "NON_UNIQUE", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "INDEX_QUALIFIER", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "INDEX_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "TYPE", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "ORDINAL_POSITION", SQL_SMALLINT, 50 }, + { "SYSTEM", "STATISTICS", "COLUMN_NAME", SCOL_VARCHAR, 255 }, + { "SYSTEM", "STATISTICS", "ASC_OR_DESC", SCOL_CHAR, 1 }, + { "SYSTEM", "STATISTICS", "CARDINALITY", SQL_INTEGER, 50 }, + { "SYSTEM", "STATISTICS", "PAGES", SQL_INTEGER, 50 }, + { "SYSTEM", "STATISTICS", "FILTER_CONDITION", SCOL_VARCHAR, 255 } +}; + +/** + * Internal return statistic information on table indices. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param itype type of index information + * @param resv reserved + * @result ODBC error code + */ + +static SQLRETURN +drvstatistics(SQLHSTMT stmt, SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT itype, SQLUSMALLINT resv) +{ + SQLRETURN sret; + STMT *s; + DBC *d; + int i, asize, ret, nrows, ncols, offs, namec, uniquec, addipk = 0; + PTRDIFF_T size; + char **rowp, *errp = NULL, *sql, tname[512]; + + sret = mkresultset(stmt, statSpec2, array_size(statSpec2), + statSpec3, array_size(statSpec3), &asize); + if (sret != SQL_SUCCESS) { + return sret; + } + s = (STMT *) stmt; + d = (DBC *) s->dbc; + if (!table || table[0] == '\0' || table[0] == '%') { + setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (tableLen == SQL_NTS) { + size = sizeof (tname) - 1; + } else { + size = min(sizeof (tname) - 1, tableLen); + } + strncpy(tname, (char *) table, size); + tname[size] = '\0'; + unescpat(tname); + sret = starttran(s); + if (sret != SQL_SUCCESS) { + return sret; + } + /* + * Try integer primary key (autoincrement) first + */ + if (itype == SQL_INDEX_UNIQUE || itype == SQL_INDEX_ALL) { + rowp = 0; + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", tname); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, + &nrows, &ncols, NULL); + sqlite4_free(0, sql); + } + if (ret == SQLITE4_OK) { + int colid, typec, npk = 0; + + namec = findcol(rowp, ncols, "name"); + uniquec = findcol(rowp, ncols, "pk"); + typec = findcol(rowp, ncols, "type"); + colid = findcol(rowp, ncols, "cid"); + if (namec < 0 || uniquec < 0 || typec < 0 || colid < 0) { + goto noipk; + } + for (i = 1; i <= nrows; i++) { + if (*rowp[i * ncols + uniquec] != '0' && + strlen(rowp[i * ncols + typec]) == 7 && + strncasecmp(rowp[i * ncols + typec], "integer", 7) + == 0) { + npk++; + } + } + if (npk == 1) { + addipk = 1; + } + } +noipk: + freerows(rowp); + } + sql = sqlite4_mprintf(0, "PRAGMA index_list(%Q)", tname); + if (!sql) { + return nomem(s); + } + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); + sqlite4_free(0, sql); + if (ret != SQLITE4_OK) { + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", ret); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + return SQL_ERROR; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + size = 0; + namec = findcol(rowp, ncols, "name"); + uniquec = findcol(rowp, ncols, "unique"); + if (namec < 0 || uniquec < 0) { + goto nodata; + } + for (i = 1; i <= nrows; i++) { + int nnrows, nncols; + char **rowpp; + int isuniq; + + isuniq = *rowp[i * ncols + uniquec] != '0'; + if (isuniq || itype == SQL_INDEX_ALL) { + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp[i * ncols + namec]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret == SQLITE4_OK) { + size += nnrows; + freerows(rowpp); + } + } + } +nodata: + if (addipk) { + size++; + } + if (size == 0) { + freerows(rowp); + return SQL_SUCCESS; + } + s->nrows = size; + size = (size + 1) * asize; + s->rows = xmalloc((size + 1) * sizeof (char *)); + if (!s->rows) { + s->nrows = 0; + return nomem(s); + } + s->rows[0] = (char *) size; + s->rows += 1; + memset(s->rows, 0, sizeof (char *) * size); + s->rowfree = freerows; + offs = 0; + if (addipk) { + char **rowpp = 0; + int nrows2, ncols2; + + sql = sqlite4_mprintf(0, "PRAGMA table_info(%Q)", tname); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nrows2, &ncols2, NULL); + sqlite4_free(0, sql); + } + if (ret == SQLITE4_OK) { + int colid, typec, roffs, namecc, uniquecc; + + namecc = findcol(rowpp, ncols2, "name"); + uniquecc = findcol(rowpp, ncols2, "pk"); + typec = findcol(rowpp, ncols2, "type"); + colid = findcol(rowpp, ncols2, "cid"); + if (namecc < 0 || uniquecc < 0 || typec < 0 || colid < 0) { + addipk = 0; + s->nrows--; + goto nodata2; + } + for (i = 1; i <= nrows2; i++) { + if (*rowpp[i * ncols2 + uniquecc] != '0' && + strlen(rowpp[i * ncols2 + typec]) == 7 && + strncasecmp(rowpp[i * ncols2 + typec], "integer", 7) + == 0) { + break; + } + } + if (i > nrows2) { + addipk = 0; + s->nrows--; + goto nodata2; + } + roffs = s->ncols; + s->rows[roffs + 0] = xstrdup(""); +#if defined(_WIN32) || defined(_WIN64) + s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : ""); +#else + s->rows[roffs + 1] = xstrdup(""); +#endif + s->rows[roffs + 2] = xstrdup(tname); + s->rows[roffs + 3] = xstrdup(stringify(SQL_FALSE)); + s->rows[roffs + 5] = xstrdup("sqlite_autoindex_0"); + s->rows[roffs + 6] = xstrdup(stringify(SQL_INDEX_OTHER)); + s->rows[roffs + 7] = xstrdup("1"); + s->rows[roffs + 8] = xstrdup(rowpp[i * ncols2 + namecc]); + s->rows[roffs + 9] = xstrdup("A"); + } +nodata2: + freerows(rowpp); + } + for (i = 1; i <= nrows; i++) { + int nnrows, nncols; + char **rowpp = 0; + + if (*rowp[i * ncols + uniquec] != '0' || itype == SQL_INDEX_ALL) { + int k; + + ret = SQLITE4_ERROR; + sql = sqlite4_mprintf(0, "PRAGMA index_info(%Q)", + rowp[i * ncols + namec]); + if (sql) { + dbtraceapi(d, "sqlite4_get_table", sql); + ret = sqlite4_get_table(d->sqlite, sql, &rowpp, + &nnrows, &nncols, NULL); + sqlite4_free(0, sql); + } + if (ret != SQLITE4_OK) { + continue; + } + for (k = 0; nnrows && k < nncols; k++) { + if (strcmp(rowpp[k], "name") == 0) { + int m; + + for (m = 1; m <= nnrows; m++) { + int roffs = (offs + addipk + m) * s->ncols; + int isuniq; + + isuniq = *rowp[i * ncols + uniquec] != '0'; + s->rows[roffs + 0] = xstrdup(""); + s->rows[roffs + 1] = xstrdup(""); + s->rows[roffs + 2] = xstrdup(tname); + if (isuniq) { + s->rows[roffs + 3] = xstrdup(stringify(SQL_FALSE)); + } else { + s->rows[roffs + 3] = xstrdup(stringify(SQL_TRUE)); + } + s->rows[roffs + 5] = xstrdup(rowp[i * ncols + namec]); + s->rows[roffs + 6] = + xstrdup(stringify(SQL_INDEX_OTHER)); + s->rows[roffs + 8] = xstrdup(rowpp[m * nncols + k]); + s->rows[roffs + 9] = xstrdup("A"); + } + } else if (strcmp(rowpp[k], "seqno") == 0) { + int m; + + for (m = 1; m <= nnrows; m++) { + int roffs = (offs + addipk + m) * s->ncols; + int pos = m - 1; + char buf[32]; + + sscanf(rowpp[m * nncols + k], "%d", &pos); + sprintf(buf, "%d", pos + 1); + s->rows[roffs + 7] = xstrdup(buf); + } + } + } + offs += nnrows; + freerows(rowpp); + } + } + freerows(rowp); + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Return statistic information on table indices. + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param itype type of index information + * @param resv reserved + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLStatistics(SQLHSTMT stmt, SQLCHAR *cat, SQLSMALLINT catLen, + SQLCHAR *schema, SQLSMALLINT schemaLen, + SQLCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT itype, SQLUSMALLINT resv) +{ +#if defined(_WIN32) || defined(_WIN64) + char *c = NULL, *s = NULL, *t = NULL; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvstatistics(stmt, cat, catLen, schema, schemaLen, + table, tableLen, itype, resv); + goto done2; + } + if (cat) { + c = wmb_to_utf_c((char *) cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = wmb_to_utf_c((char *) schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = wmb_to_utf_c((char *) table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvstatistics(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, itype, resv); +#else + ret = drvstatistics(stmt, cat, catLen, schema, schemaLen, + table, tableLen, itype, resv); +#endif +#if defined(_WIN32) || defined(_WIN64) +done: + uc_free(t); + uc_free(s); + uc_free(c); +done2: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Return statistic information on table indices (UNICODE version). + * @param stmt statement handle + * @param cat catalog name/pattern or NULL + * @param catLen length of catalog name/pattern or SQL_NTS + * @param schema schema name/pattern or NULL + * @param schemaLen length of schema name/pattern or SQL_NTS + * @param table table name/pattern or NULL + * @param tableLen length of table name/pattern or SQL_NTS + * @param itype type of index information + * @param resv reserved + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLStatisticsW(SQLHSTMT stmt, SQLWCHAR *cat, SQLSMALLINT catLen, + SQLWCHAR *schema, SQLSMALLINT schemaLen, + SQLWCHAR *table, SQLSMALLINT tableLen, + SQLUSMALLINT itype, SQLUSMALLINT resv) +{ + char *c = NULL, *s = NULL, *t = NULL; + SQLRETURN ret; + + HSTMT_LOCK(stmt); + if (cat) { + c = uc_to_utf_c(cat, catLen); + if (!c) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (schema) { + s = uc_to_utf_c(schema, schemaLen); + if (!s) { + ret = nomem((STMT *) stmt); + goto done; + } + } + if (table) { + t = uc_to_utf_c(table, tableLen); + if (!t) { + ret = nomem((STMT *) stmt); + goto done; + } + } + ret = drvstatistics(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS, + (SQLCHAR *) t, SQL_NTS, itype, resv); +done: + uc_free(t); + uc_free(s); + uc_free(c); + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Retrieve row data after fetch. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param type output type + * @param val output buffer + * @param len length of output buffer + * @param lenp output length + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLGetData(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, + SQLPOINTER val, SQLLEN len, SQLLEN *lenp) +{ + STMT *s; + SQLRETURN ret = SQL_ERROR; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (col == 0 && s->bkmrk != SQL_UB_OFF) { + if (s->bkmrk == SQL_UB_ON && type == SQL_C_BOOKMARK) { + *((SQLINTEGER *) val) = s->rowp; + if (lenp) { + *lenp = sizeof (SQLINTEGER); + } + ret = SQL_SUCCESS; + goto done; + } else if (s->bkmrk == SQL_UB_VARIABLE && type == SQL_C_VARBOOKMARK) { + if (s->has_rowid >= 0) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (s->rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + *((sqlite4_int64 *) val) = strtol(*data, &endp, 0); +#else + *((sqlite4_int64 *) val) = strtoll(*data, &endp, 0); +#endif + } else { + *((sqlite4_int64 *) val) = s->rowp; + } + if (lenp) { + *lenp = sizeof (sqlite4_int64); + } + ret = SQL_SUCCESS; + goto done; + } + } + if (col < 1 || col > s->ncols) { + setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); + goto done; + } + --col; + ret = getrowdata(s, col, type, val, len, lenp, 1); +done: + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Internal: fetch and bind from statement's current row + * @param s statement pointer + * @param rsi rowset index + * @result ODBC error code + */ + +static SQLRETURN +dofetchbind(STMT *s, int rsi) +{ + int ret, i, withinfo = 0; + + s->row_status0[rsi] = SQL_ROW_SUCCESS; + if (s->bkmrk != SQL_UB_OFF && s->bkmrkcol.valp) { + int bsize = sizeof (SQLINTEGER); + + if (s->bkmrkcol.type == SQL_C_VARBOOKMARK) { + SQLPOINTER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bind_type * rsi); + } else { + val = (SQLPOINTER) + ((char *) s->bkmrkcol.valp + s->bkmrkcol.max * rsi); + } + if (s->bind_offs) { + val = (SQLPOINTER) ((char *) val + *s->bind_offs); + } + if (s->has_rowid >= 0) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (s->rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + *(sqlite4_int64 *) val = strtol(*data, &endp, 0); +#else + *(sqlite4_int64 *) val = strtoll(*data, &endp, 0); +#endif + } else { + *(sqlite4_int64 *) val = s->rowp; + } + bsize = sizeof (sqlite4_int64); + } else { + SQLINTEGER *val; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + val = (SQLINTEGER *) + ((char *) s->bkmrkcol.valp + s->bind_type * rsi); + } else { + val = (SQLINTEGER *) s->bkmrkcol.valp + rsi; + } + if (s->bind_offs) { + val = (SQLINTEGER *) ((char *) val + *s->bind_offs); + } + *val = s->rowp; + } + if (s->bkmrkcol.lenp) { + SQLLEN *ival; + + if (s->bind_type != SQL_BIND_BY_COLUMN) { + ival = (SQLLEN *) + ((char *) s->bkmrkcol.lenp + s->bind_type * rsi); + } else { + ival = &s->bkmrkcol.lenp[rsi]; + } + if (s->bind_offs) { + ival = (SQLLEN *) ((char *) ival + *s->bind_offs); + } + *ival = bsize; + } + } + ret = SQL_SUCCESS; + for (i = 0; s->bindcols && i < s->ncols; i++) { + BINDCOL *b = &s->bindcols[i]; + SQLPOINTER dp = 0; + SQLLEN *lp = 0; + + b->offs = 0; + if (b->valp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi); + } else { + dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi); + } + if (s->bind_offs) { + dp = (SQLPOINTER) ((char *) dp + *s->bind_offs); + } + } + if (b->lenp) { + if (s->bind_type != SQL_BIND_BY_COLUMN) { + lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi); + } else { + lp = b->lenp + rsi; + } + if (s->bind_offs) { + lp = (SQLLEN *) ((char *) lp + *s->bind_offs); + } + } + if (dp || lp) { + ret = getrowdata(s, (SQLUSMALLINT) i, b->type, dp, b->max, lp, 0); + if (!SQL_SUCCEEDED(ret)) { + s->row_status0[rsi] = SQL_ROW_ERROR; + break; + } + if (ret != SQL_SUCCESS) { + withinfo = 1; +#ifdef SQL_ROW_SUCCESS_WITH_INFO + s->row_status0[rsi] = SQL_ROW_SUCCESS_WITH_INFO; +#endif + } + } + } + if (SQL_SUCCEEDED(ret)) { + ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS; + } + return ret; +} + +/** + * Internal fetch function for SQLFetchScroll() and SQLExtendedFetch(). + * @param stmt statement handle + * @param orient fetch direction + * @param offset offset for fetch direction + * @result ODBC error code + */ + +static SQLRETURN +drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) +{ + STMT *s; + int i, withinfo = 0; + SQLRETURN ret; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + for (i = 0; i < s->rowset_size; i++) { + s->row_status0[i] = SQL_ROW_NOROW; + } + if (s->row_status) { + memcpy(s->row_status, s->row_status0, + sizeof (SQLUSMALLINT) * s->rowset_size); + } + s->row_count0 = 0; + if (s->row_count) { + *s->row_count = s->row_count0; + } + if (!s->bindcols) { + for (i = 0; i < s->rowset_size; i++) { + s->row_status0[i] = SQL_ROW_ERROR; + } + ret = SQL_ERROR; + i = 0; + goto done2; + } + if (s->isselect != 1 && s->isselect != -1) { + setstat(s, -1, "no result set available", "24000"); + ret = SQL_ERROR; + i = s->nrows; + goto done2; + } + if (s->curtype == SQL_CURSOR_FORWARD_ONLY && orient != SQL_FETCH_NEXT) { + setstat(s, -1, "wrong fetch direction", "01000"); + ret = SQL_ERROR; + i = 0; + goto done2; + } + ret = SQL_SUCCESS; + i = 0; + if (((DBC *) (s->dbc))->cur_s4stmt == s && s->s4stmt) { + s->rowp = s->rowprs = 0; + for (; i < s->rowset_size; i++) { + if (s->max_rows && s->s4stmt_rownum + 1 >= s->max_rows) { + ret = (i == 0) ? SQL_NO_DATA : SQL_SUCCESS; + break; + } + ret = s4stmt_step(s); + if (ret != SQL_SUCCESS) { + s->row_status0[i] = SQL_ROW_ERROR; + break; + } + if (s->nrows < 1) { + break; + } + ret = dofetchbind(s, i); + if (!SQL_SUCCEEDED(ret)) { + break; + } else if (ret == SQL_SUCCESS_WITH_INFO) { + withinfo = 1; + } + } + } else if (s->rows) { + switch (orient) { + case SQL_FETCH_NEXT: + if (s->nrows < 1) { + return SQL_NO_DATA; + } + if (s->rowp < 0) { + s->rowp = -1; + } + if (s->rowp >= s->nrows) { + s->rowp = s->rowprs = s->nrows; + return SQL_NO_DATA; + } + break; + case SQL_FETCH_PRIOR: + if (s->nrows < 1 || s->rowp <= 0) { + s->rowp = s->rowprs = -1; + return SQL_NO_DATA; + } + s->rowp -= s->rowset_size + 1; + if (s->rowp < -1) { + s->rowp = s->rowprs = -1; + return SQL_NO_DATA; + } + break; + case SQL_FETCH_FIRST: + if (s->nrows < 1) { + return SQL_NO_DATA; + } + s->rowp = -1; + break; + case SQL_FETCH_LAST: + if (s->nrows < 1) { + return SQL_NO_DATA; + } + s->rowp = s->nrows - s->rowset_size; + if (--s->rowp < -1) { + s->rowp = -1; + } + break; + case SQL_FETCH_ABSOLUTE: + if (offset == 0) { + s->rowp = s->rowprs = -1; + return SQL_NO_DATA; + } else if (offset < 0) { + if (0 - offset <= s->nrows) { + s->rowp = s->nrows + offset - 1; + break; + } + s->rowp = s->rowprs = -1; + return SQL_NO_DATA; + } else if (offset > s->nrows) { + s->rowp = s->rowprs = s->nrows; + return SQL_NO_DATA; + } + s->rowp = offset - 1 - 1; + break; + case SQL_FETCH_RELATIVE: + if (offset >= 0) { + s->rowp += offset * s->rowset_size - 1; + if (s->rowp >= s->nrows) { + s->rowp = s->rowprs = s->nrows; + return SQL_NO_DATA; + } + } else { + s->rowp += offset * s->rowset_size - 1; + if (s->rowp < -1) { + s->rowp = s->rowprs = -1; + return SQL_NO_DATA; + } + } + break; + case SQL_FETCH_BOOKMARK: + if (s->bkmrk == SQL_UB_ON && !s->bkmrkptr) { + if (offset < 0 || offset >= s->nrows) { + return SQL_NO_DATA; + } + s->rowp = offset - 1; + break; + } + if (s->bkmrk != SQL_UB_OFF && s->bkmrkptr) { + int rowp; + + if (s->bkmrk == SQL_UB_VARIABLE) { + if (s->has_rowid >= 0) { + sqlite4_int64 bkmrk, rowid; + + bkmrk = *(sqlite4_int64 *) s->bkmrkptr; + for (rowp = 0; rowp < s->nrows; rowp++) { + char **data, *endp = 0; + + data = s->rows + s->ncols + (rowp * s->ncols) + + s->has_rowid; +#ifdef __osf__ + rowid = strtol(*data, &endp, 0); +#else + rowid = strtoll(*data, &endp, 0); +#endif + if (rowid == bkmrk) { + break; + } + } + } else { + rowp = *(sqlite4_int64 *) s->bkmrkptr; + } + } else { + rowp = *(int *) s->bkmrkptr; + } + if (rowp + offset < 0 || rowp + offset >= s->nrows) { + return SQL_NO_DATA; + } + s->rowp = rowp + offset - 1; + break; + } + /* fall through */ + default: + s->row_status0[0] = SQL_ROW_ERROR; + ret = SQL_ERROR; + goto done; + } + s->rowprs = s->rowp + 1; + for (; i < s->rowset_size; i++) { + ++s->rowp; + if (s->rowp < 0 || s->rowp >= s->nrows) { + break; + } + ret = dofetchbind(s, i); + if (!SQL_SUCCEEDED(ret)) { + break; + } else if (ret == SQL_SUCCESS_WITH_INFO) { + withinfo = 1; + } + } + } +done: + if (i == 0) { + if (SQL_SUCCEEDED(ret)) { + return SQL_NO_DATA; + } + return ret; + } + if (SQL_SUCCEEDED(ret)) { + ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS; + } +done2: + if (s->row_status) { + memcpy(s->row_status, s->row_status0, + sizeof (SQLUSMALLINT) * s->rowset_size); + } + s->row_count0 = i; + if (s->row_count) { + *s->row_count = s->row_count0; + } + return ret; +} + +/** + * Fetch next result row. + * @param stmt statement handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFetch(SQLHSTMT stmt) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvfetchscroll(stmt, SQL_FETCH_NEXT, 0); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Fetch result row with scrolling. + * @param stmt statement handle + * @param orient fetch direction + * @param offset offset for fetch direction + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLLEN offset) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvfetchscroll(stmt, orient, offset); + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Fetch result row with scrolling and row status. + * @param stmt statement handle + * @param orient fetch direction + * @param offset offset for fetch direction + * @param rowcount output number of fetched rows + * @param rowstatus array for row stati + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT orient, SQLROWOFFSET offset, + SQLROWSETSIZE *rowcount, SQLUSMALLINT *rowstatus) +{ + STMT *s; + SQLRETURN ret; + SQLUSMALLINT *rst; + SQLINTEGER *bkmrkptr; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + /* temporarily turn off SQL_ATTR_ROW_STATUS_PTR */ + rst = s->row_status; + s->row_status = 0; + bkmrkptr = s->bkmrkptr; + s->bkmrkptr = 0; + ret = drvfetchscroll(stmt, orient, offset); + s->row_status = rst; + s->bkmrkptr = bkmrkptr; + if (rowstatus) { + memcpy(rowstatus, s->row_status0, + sizeof (SQLUSMALLINT) * s->rowset_size); + } + if (rowcount) { + *rowcount = s->row_count0; + } + HSTMT_UNLOCK(stmt); + return ret; +} + +/** + * Return number of affected rows of HSTMT. + * @param stmt statement handle + * @param nrows output number of rows + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLRowCount(SQLHSTMT stmt, SQLLEN *nrows) +{ + STMT *s; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (nrows) { + *nrows = s->isselect ? 0 : s->nrows; + } + HSTMT_UNLOCK(stmt); + return SQL_SUCCESS; +} + +/** + * Return number of columns of result set given HSTMT. + * @param stmt statement handle + * @param ncols output number of columns + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT *ncols) +{ + STMT *s; + + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (ncols) { + *ncols = s->ncols; + } + HSTMT_UNLOCK(stmt); + return SQL_SUCCESS; +} + +/** + * Internal describe column information. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param name buffer for column name + * @param nameMax length of name buffer + * @param nameLen output length of column name + * @param type output SQL type + * @param size output column size + * @param digits output number of digits + * @param nullable output NULL allowed indicator + * @result ODBC error code + */ + +static SQLRETURN +drvdescribecol(SQLHSTMT stmt, SQLUSMALLINT col, SQLCHAR *name, + SQLSMALLINT nameMax, SQLSMALLINT *nameLen, + SQLSMALLINT *type, SQLULEN *size, + SQLSMALLINT *digits, SQLSMALLINT *nullable) +{ + STMT *s; + COL *c; + int didname = 0; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!s->cols) { + setstat(s, -1, "no columns", (*s->ov3) ? "07009" : "S1002"); + return SQL_ERROR; + } + if (col < 1 || col > s->ncols) { + setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); + return SQL_ERROR; + } + c = s->cols + col - 1; + if (name && nameMax > 0) { + strncpy((char *) name, c->column, nameMax); + name[nameMax - 1] = '\0'; + didname = 1; + } + if (nameLen) { + if (didname) { + *nameLen = strlen((char *) name); + } else { + *nameLen = strlen(c->column); + } + } + if (type) { + *type = c->type; +#ifdef WINTERFACE + if (s->nowchar[0] || s->nowchar[1]) { + switch (c->type) { + case SQL_WCHAR: + *type = SQL_CHAR; + break; + case SQL_WVARCHAR: + *type = SQL_VARCHAR; + break; +#ifdef SQL_LONGVARCHAR + case SQL_WLONGVARCHAR: + *type = SQL_LONGVARCHAR; + break; +#endif + } + } +#endif + } + if (size) { + *size = c->size; + } + if (digits) { + *digits = 0; + } + if (nullable) { + *nullable = 1; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Describe column information. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param name buffer for column name + * @param nameMax length of name buffer + * @param nameLen output length of column name + * @param type output SQL type + * @param size output column size + * @param digits output number of digits + * @param nullable output NULL allowed indicator + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDescribeCol(SQLHSTMT stmt, SQLUSMALLINT col, SQLCHAR *name, + SQLSMALLINT nameMax, SQLSMALLINT *nameLen, + SQLSMALLINT *type, SQLULEN *size, + SQLSMALLINT *digits, SQLSMALLINT *nullable) +{ +#if defined(_WIN32) || defined(_WIN64) + SQLSMALLINT len = 0; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvdescribecol(stmt, col, name, nameMax, nameLen, + type, size, digits, nullable); + goto done; + } + ret = drvdescribecol(stmt, col, name, nameMax, + &len, type, size, digits, nullable); + if (ret == SQL_SUCCESS) { + if (name) { + if (len > 0) { + SQLCHAR *n = NULL; + + n = (SQLCHAR *) utf_to_wmb((char *) name, len); + if (n) { + strncpy((char *) name, (char *) n, nameMax); + n[len] = 0; + len = min(nameMax, strlen((char *) n)); + uc_free(n); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (nameMax > 0) { + name[0] = 0; + } + } + } else { + STMT *s = (STMT *) stmt; + COL *c = s->cols + col - 1; + + len = 0; + if (c->column) { + len = strlen(c->column); + } + } + if (nameLen) { + *nameLen = len; + } + } +done: + ; +#else + ret = drvdescribecol(stmt, col, name, nameMax, nameLen, + type, size, digits, nullable); +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Describe column information (UNICODE version). + * @param stmt statement handle + * @param col column number, starting at 1 + * @param name buffer for column name + * @param nameMax length of name buffer + * @param nameLen output length of column name + * @param type output SQL type + * @param size output column size + * @param digits output number of digits + * @param nullable output NULL allowed indicator + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDescribeColW(SQLHSTMT stmt, SQLUSMALLINT col, SQLWCHAR *name, + SQLSMALLINT nameMax, SQLSMALLINT *nameLen, + SQLSMALLINT *type, SQLULEN *size, + SQLSMALLINT *digits, SQLSMALLINT *nullable) +{ + SQLRETURN ret; + SQLSMALLINT len = 0; + + HSTMT_LOCK(stmt); + ret = drvdescribecol(stmt, col, (SQLCHAR *) name, + (SQLSMALLINT) (nameMax * sizeof (SQLWCHAR)), + &len, type, size, digits, nullable); + if (ret == SQL_SUCCESS) { + if (name) { + if (len > 0) { + SQLWCHAR *n = NULL; + + n = uc_from_utf((SQLCHAR *) name, len); + if (n) { + uc_strncpy(name, n, nameMax); + n[len] = 0; + len = min(nameMax, uc_strlen(n)); + uc_free(n); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (nameMax > 0) { + name[0] = 0; + } + } + } else { + STMT *s = (STMT *) stmt; + COL *c = s->cols + col - 1; + + len = 0; + if (c->column) { + len = strlen(c->column); + } + } + if (nameLen) { + *nameLen = len; + } + } + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal retrieve column attributes. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +static SQLRETURN +drvcolattributes(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + SQLLEN *val2) +{ + STMT *s; + COL *c; + SQLSMALLINT dummy; + char *valc = (char *) val; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!s->cols) { + return SQL_ERROR; + } + if (!valLen) { + valLen = &dummy; + } + if (id == SQL_COLUMN_COUNT) { + if (val2) { + *val2 = s->ncols; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + } + if (id == SQL_COLUMN_TYPE && col == 0) { + if (val2) { + *val2 = SQL_INTEGER; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + } +#ifdef SQL_DESC_OCTET_LENGTH + if (id == SQL_DESC_OCTET_LENGTH && col == 0) { + if (val2) { + *val2 = 4; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + } +#endif + if (col < 1 || col > s->ncols) { + setstat(s, -1, "invalid column", (*s->ov3) ? "07009": "S1002"); + return SQL_ERROR; + } + c = s->cols + col - 1; + + switch (id) { + case SQL_COLUMN_LABEL: + if (c->label) { + if (valc && valMax > 0) { + strncpy(valc, c->label, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->label); + goto checkLen; + } + /* fall through */ + case SQL_COLUMN_NAME: + case SQL_DESC_NAME: + if (valc && valMax > 0) { + strncpy(valc, c->column, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->column); +checkLen: + if (*valLen >= valMax) { + setstat(s, -1, "data right truncated", "01004"); + return SQL_SUCCESS_WITH_INFO; + } + return SQL_SUCCESS; +#ifdef SQL_DESC_BASE_COLUMN_NAME + if (strchr(c->column, '(') || strchr(c->column, ')')) { + valc[0] = '\0'; + *valLen = 0; + } else if (valc && valMax > 0) { + strncpy(valc, c->column, valMax); + valc[valMax - 1] = '\0'; + *valLen = strlen(c->column); + } + goto checkLen; +#endif + case SQL_COLUMN_TYPE: + case SQL_DESC_TYPE: +#ifdef WINTERFACE + { + int type = c->type; + + if (s->nowchar[0] || s->nowchar[1]) { + switch (type) { + case SQL_WCHAR: + type = SQL_CHAR; + break; + case SQL_WVARCHAR: + type = SQL_VARCHAR; + break; +#ifdef SQL_LONGVARCHAR + case SQL_WLONGVARCHAR: + type = SQL_LONGVARCHAR; + break; + } + } + if (val2) { + *val2 = type; + } +#endif + } +#else + if (val2) { + *val2 = c->type; + } +#endif + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_DISPLAY_SIZE: + if (val2) { + *val2 = c->size; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_UNSIGNED: + if (val2) { + *val2 = c->nosign ? SQL_TRUE : SQL_FALSE; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_SCALE: + case SQL_DESC_SCALE: + if (val2) { + *val2 = c->scale; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_PRECISION: + case SQL_DESC_PRECISION: + if (val2) { + switch (c->type) { + case SQL_SMALLINT: + *val2 = 5; + break; + case SQL_INTEGER: + *val2 = 10; + break; + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + *val2 = 15; + break; + case SQL_DATE: + *val2 = 0; + break; + case SQL_TIME: + *val2 = 0; + break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + case SQL_TIMESTAMP: + *val2 = (c->prec >= 0 && c->prec <= 3) ? c->prec : 3; + break; + default: + *val2 = c->prec; + break; + } + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_MONEY: + if (val2) { + *val2 = SQL_FALSE; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_AUTO_INCREMENT: + if (val2) { + *val2 = c->autoinc; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_LENGTH: + case SQL_DESC_LENGTH: + if (val2) { + *val2 = c->size; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_NULLABLE: + case SQL_DESC_NULLABLE: + if (val2) { + *val2 = c->notnull; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_SEARCHABLE: + if (val2) { + *val2 = SQL_SEARCHABLE; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_CASE_SENSITIVE: + if (val2) { + *val2 = SQL_TRUE; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_UPDATABLE: + if (val2) { + *val2 = SQL_TRUE; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_DESC_COUNT: + if (val2) { + *val2 = s->ncols; + } + *valLen = sizeof (int); + return SQL_SUCCESS; + case SQL_COLUMN_TYPE_NAME: { + char *p = NULL, *tn = c->typename ? c->typename : "varchar"; + +#ifdef WINTERFACE + if (c->type == SQL_WCHAR || + c->type == SQL_WVARCHAR || + c->type == SQL_WLONGVARCHAR) { + if (!(s->nowchar[0] || s->nowchar[1])) { + if (strcasecmp(tn, "varchar") == 0) { + tn = "wvarchar"; + } + } + } +#endif + if (valc && valMax > 0) { + strncpy(valc, tn, valMax); + valc[valMax - 1] = '\0'; + p = strchr(valc, '('); + if (p) { + *p = '\0'; + while (p > valc && ISSPACE(p[-1])) { + --p; + *p = '\0'; + } + } + *valLen = strlen(valc); + } else { + *valLen = strlen(tn); + p = strchr(tn, '('); + if (p) { + *valLen = p - tn; + while (p > tn && ISSPACE(p[-1])) { + --p; + *valLen -= 1; + } + } + } + goto checkLen; + } + case SQL_COLUMN_OWNER_NAME: + case SQL_COLUMN_QUALIFIER_NAME: { + char *z = ""; + + if (valc && valMax > 0) { + strncpy(valc, z, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(z); + goto checkLen; + } + case SQL_COLUMN_TABLE_NAME: +#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME) + case SQL_DESC_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif + if (valc && valMax > 0) { + strncpy(valc, c->table, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->table); + goto checkLen; +#ifdef SQL_DESC_NUM_PREC_RADIX + case SQL_DESC_NUM_PREC_RADIX: + if (val2) { + switch (c->type) { +#ifdef WINTERFACE + case SQL_WCHAR: + case SQL_WVARCHAR: +#ifdef SQL_LONGVARCHAR + case SQL_WLONGVARCHAR: +#endif +#endif + case SQL_CHAR: + case SQL_VARCHAR: +#ifdef SQL_LONGVARCHAR + case SQL_LONGVARCHAR: +#endif + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + *val2 = 0; + break; + default: + *val2 = 2; + } + } + *valLen = sizeof (int); + return SQL_SUCCESS; +#endif + } + setstat(s, -1, "unsupported column attributes %d", "HY091", id); + return SQL_ERROR; +} + +#ifndef WINTERFACE +/** + * Retrieve column attributes. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColAttributes(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + SQLLEN *val2) +{ +#if defined(_WIN32) || defined(_WIN64) + SQLSMALLINT len = 0; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvcolattributes(stmt, col, id, val, valMax, valLen, val2); + goto done; + } + ret = drvcolattributes(stmt, col, id, val, valMax, &len, val2); + if (SQL_SUCCEEDED(ret)) { + char *v = NULL; + + switch (id) { + case SQL_COLUMN_LABEL: + case SQL_COLUMN_NAME: + case SQL_DESC_NAME: + case SQL_COLUMN_TYPE_NAME: + case SQL_COLUMN_OWNER_NAME: + case SQL_COLUMN_QUALIFIER_NAME: + case SQL_COLUMN_TABLE_NAME: +#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME) + case SQL_DESC_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_COLUMN_NAME + case SQL_DESC_BASE_COLUMN_NAME: +#endif +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif + if (val && valMax > 0) { + int vmax = valMax; + + v = utf_to_wmb((char *) val, SQL_NTS); + if (v) { + strncpy(val, v, vmax); + len = min(vmax, strlen(v)); + uc_free(v); + } + if (vmax > 0) { + v = (char *) val; + v[vmax - 1] = '\0'; + } + } + if (len <= 0) { + len = 0; + } + break; + } + if (valLen) { + *valLen = len; + } + } +done: + ; +#else + ret = drvcolattributes(stmt, col, id, val, valMax, valLen, val2); +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve column attributes (UNICODE version). + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColAttributesW(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + SQLLEN *val2) +{ + SQLRETURN ret; + SQLSMALLINT len = 0; + + HSTMT_LOCK(stmt); + ret = drvcolattributes(stmt, col, id, val, valMax, &len, val2); + if (SQL_SUCCEEDED(ret)) { + SQLWCHAR *v = NULL; + + switch (id) { + case SQL_COLUMN_LABEL: + case SQL_COLUMN_NAME: + case SQL_DESC_NAME: + case SQL_COLUMN_TYPE_NAME: + case SQL_COLUMN_OWNER_NAME: + case SQL_COLUMN_QUALIFIER_NAME: + case SQL_COLUMN_TABLE_NAME: +#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME) + case SQL_DESC_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_COLUMN_NAME + case SQL_DESC_BASE_COLUMN_NAME: +#endif +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif + if (val && valMax > 0) { + int vmax = valMax / sizeof (SQLWCHAR); + + v = uc_from_utf((SQLCHAR *) val, SQL_NTS); + if (v) { + uc_strncpy(val, v, vmax); + len = min(vmax, uc_strlen(v)); + uc_free(v); + len *= sizeof (SQLWCHAR); + } + if (vmax > 0) { + v = (SQLWCHAR *) val; + v[vmax - 1] = '\0'; + } + } + if (len <= 0) { + len = 0; + } + break; + } + if (valLen) { + *valLen = len; + } + } + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal retrieve column attributes. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +static SQLRETURN +drvcolattribute(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + SQLPOINTER val2) +{ + STMT *s; + COL *c; + int v = 0; + char *valc = (char *) val; + SQLSMALLINT dummy; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (!s->cols) { + return SQL_ERROR; + } + if (col < 1 || col > s->ncols) { + setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); + return SQL_ERROR; + } + if (!valLen) { + valLen = &dummy; + } + c = s->cols + col - 1; + switch (id) { + case SQL_DESC_COUNT: + v = s->ncols; + break; + case SQL_DESC_CATALOG_NAME: + if (valc && valMax > 0) { + strncpy(valc, c->db, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->db); +checkLen: + if (*valLen >= valMax) { + setstat(s, -1, "data right truncated", "01004"); + return SQL_SUCCESS_WITH_INFO; + } + break; + case SQL_COLUMN_LENGTH: + case SQL_DESC_LENGTH: + v = c->size; + break; + case SQL_COLUMN_LABEL: + if (c->label) { + if (valc && valMax > 0) { + strncpy(valc, c->label, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->label); + goto checkLen; + } + /* fall through */ + case SQL_COLUMN_NAME: + case SQL_DESC_NAME: + if (valc && valMax > 0) { + strncpy(valc, c->column, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->column); + goto checkLen; + case SQL_DESC_SCHEMA_NAME: { + char *z = ""; + + if (valc && valMax > 0) { + strncpy(valc, z, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(z); + goto checkLen; + } +#ifdef SQL_DESC_BASE_COLUMN_NAME + case SQL_DESC_BASE_COLUMN_NAME: + if (strchr(c->column, '(') || strchr(c->column, ')')) { + valc[0] = '\0'; + *valLen = 0; + } else if (valc && valMax > 0) { + strncpy(valc, c->column, valMax); + valc[valMax - 1] = '\0'; + *valLen = strlen(c->column); + } + goto checkLen; +#endif + case SQL_DESC_TYPE_NAME: { + char *p = NULL, *tn = c->typename ? c->typename : "varchar"; + +#ifdef WINTERFACE + if (c->type == SQL_WCHAR || + c->type == SQL_WVARCHAR || + c->type == SQL_WLONGVARCHAR) { + if (!(s->nowchar[0] || s->nowchar[1])) { + if (strcasecmp(tn, "varchar") == 0) { + tn = "wvarchar"; + } + } + } +#endif + if (valc && valMax > 0) { + strncpy(valc, tn, valMax); + valc[valMax - 1] = '\0'; + p = strchr(valc, '('); + if (p) { + *p = '\0'; + while (p > valc && ISSPACE(p[-1])) { + --p; + *p = '\0'; + } + } + *valLen = strlen(valc); + } else { + *valLen = strlen(tn); + p = strchr(tn, '('); + if (p) { + *valLen = p - tn; + while (p > tn && ISSPACE(p[-1])) { + --p; + *valLen -= 1; + } + } + } + goto checkLen; + } + case SQL_DESC_OCTET_LENGTH: + v = c->size; +#ifdef WINTERFACE + if (c->type == SQL_WCHAR || + c->type == SQL_WVARCHAR || + c->type == SQL_WLONGVARCHAR) { + if (!(s->nowchar[0] || s->nowchar[1])) { + v *= sizeof (SQLWCHAR); + } + } +#endif + break; +#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME) + case SQL_COLUMN_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif + case SQL_DESC_TABLE_NAME: + if (valc && valMax > 0) { + strncpy(valc, c->table, valMax); + valc[valMax - 1] = '\0'; + } + *valLen = strlen(c->table); + goto checkLen; + case SQL_DESC_TYPE: + v = c->type; +#ifdef WINTERFACE + if (s->nowchar[0] || s->nowchar[1]) { + switch (v) { + case SQL_WCHAR: + v = SQL_CHAR; + break; + case SQL_WVARCHAR: + v = SQL_VARCHAR; + break; +#ifdef SQL_LONGVARCHAR + case SQL_WLONGVARCHAR: + v = SQL_LONGVARCHAR; + break; +#endif + } + } +#endif + break; + case SQL_DESC_CONCISE_TYPE: + switch (c->type) { + case SQL_INTEGER: + v = SQL_C_LONG; + break; + case SQL_TINYINT: + v = SQL_C_TINYINT; + break; + case SQL_SMALLINT: + v = SQL_C_SHORT; + break; + case SQL_FLOAT: + v = SQL_C_FLOAT; + break; + case SQL_DOUBLE: + v = SQL_C_DOUBLE; + break; + case SQL_TIMESTAMP: + v = SQL_C_TIMESTAMP; + break; + case SQL_TIME: + v = SQL_C_TIME; + break; + case SQL_DATE: + v = SQL_C_DATE; + break; +#ifdef SQL_C_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: + v = SQL_C_TYPE_TIMESTAMP; + break; +#endif +#ifdef SQL_C_TYPE_TIME + case SQL_TYPE_TIME: + v = SQL_C_TYPE_TIME; + break; +#endif +#ifdef SQL_C_TYPE_DATE + case SQL_TYPE_DATE: + v = SQL_C_TYPE_DATE; + break; +#endif +#ifdef SQL_BIT + case SQL_BIT: + v = SQL_C_BIT; + break; +#endif +#ifdef SQL_BIGINT + case SQL_BIGINT: + v = SQL_C_SBIGINT; + break; +#endif + default: +#ifdef WINTERFACE + v = (s->nowchar[0] || s->nowchar[1]) ? SQL_C_CHAR : SQL_C_WCHAR; +#else + v = SQL_C_CHAR; +#endif + break; + } + break; + case SQL_DESC_UPDATABLE: + v = SQL_TRUE; + break; + case SQL_COLUMN_DISPLAY_SIZE: + v = c->size; + break; + case SQL_COLUMN_UNSIGNED: + v = c->nosign ? SQL_TRUE : SQL_FALSE; + break; + case SQL_COLUMN_SEARCHABLE: + v = SQL_SEARCHABLE; + break; + case SQL_COLUMN_SCALE: + case SQL_DESC_SCALE: + v = c->scale; + break; + case SQL_COLUMN_PRECISION: + case SQL_DESC_PRECISION: + switch (c->type) { + case SQL_SMALLINT: + v = 5; + break; + case SQL_INTEGER: + v = 10; + break; + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + v = 15; + break; + case SQL_DATE: + v = 0; + break; + case SQL_TIME: + v = 0; + break; +#ifdef SQL_TYPE_TIMESTAMP + case SQL_TYPE_TIMESTAMP: +#endif + case SQL_TIMESTAMP: + v = (c->prec >= 0 && c->prec <= 3) ? c->prec : 3; + break; + default: + v = c->prec; + break; + } + break; + case SQL_COLUMN_MONEY: + v = SQL_FALSE; + break; + case SQL_COLUMN_AUTO_INCREMENT: + v = c->autoinc; + break; + case SQL_DESC_NULLABLE: + v = c->notnull; + break; +#ifdef SQL_DESC_NUM_PREC_RADIX + case SQL_DESC_NUM_PREC_RADIX: + switch (c->type) { +#ifdef WINTERFACE + case SQL_WCHAR: + case SQL_WVARCHAR: +#ifdef SQL_LONGVARCHAR + case SQL_WLONGVARCHAR: +#endif +#endif + case SQL_CHAR: + case SQL_VARCHAR: +#ifdef SQL_LONGVARCHAR + case SQL_LONGVARCHAR: +#endif + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + v = 0; + break; + default: + v = 2; + } + break; +#endif + default: + setstat(s, -1, "unsupported column attribute %d", "HY091", id); + return SQL_ERROR; + } + if (val2) { + *(SQLLEN *) val2 = v; + } + return SQL_SUCCESS; +} + +#ifndef WINTERFACE +/** + * Retrieve column attributes. + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + COLATTRIBUTE_LAST_ARG_TYPE val2) +{ +#if defined(_WIN32) || defined(_WIN64) + SQLSMALLINT len = 0; +#endif + SQLRETURN ret; + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvcolattribute(stmt, col, id, val, valMax, valLen, + (SQLPOINTER) val2); + goto done; + } + ret = drvcolattribute(stmt, col, id, val, valMax, &len, + (SQLPOINTER) val2); + if (SQL_SUCCEEDED(ret)) { + char *v = NULL; + + switch (id) { + case SQL_DESC_SCHEMA_NAME: + case SQL_DESC_CATALOG_NAME: + case SQL_COLUMN_LABEL: + case SQL_DESC_NAME: + case SQL_DESC_TABLE_NAME: +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_COLUMN_NAME + case SQL_DESC_BASE_COLUMN_NAME: +#endif + case SQL_DESC_TYPE_NAME: + if (val && valMax > 0) { + int vmax = valMax; + + v = utf_to_wmb((char *) val, SQL_NTS); + if (v) { + strncpy(val, v, vmax); + len = min(vmax, strlen(v)); + uc_free(v); + } + if (vmax > 0) { + v = (char *) val; + v[vmax - 1] = '\0'; + } + } + if (len <= 0) { + len = 0; + } + break; + } + if (valLen) { + *valLen = len; + } + } +done: + ; +#else + ret = drvcolattribute(stmt, col, id, val, valMax, valLen, + (SQLPOINTER) val2); +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Retrieve column attributes (UNICODE version). + * @param stmt statement handle + * @param col column number, starting at 1 + * @param id attribute id + * @param val output buffer + * @param valMax length of output buffer + * @param valLen output length + * @param val2 integer output buffer + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLColAttributeW(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id, + SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen, + COLATTRIBUTE_LAST_ARG_TYPE val2) +{ + SQLRETURN ret; + SQLSMALLINT len = 0; + + HSTMT_LOCK(stmt); + ret = drvcolattribute(stmt, col, id, val, valMax, &len, + (SQLPOINTER) val2); + if (SQL_SUCCEEDED(ret)) { + SQLWCHAR *v = NULL; + + switch (id) { + case SQL_DESC_SCHEMA_NAME: + case SQL_DESC_CATALOG_NAME: + case SQL_COLUMN_LABEL: + case SQL_DESC_NAME: + case SQL_DESC_TABLE_NAME: +#ifdef SQL_DESC_BASE_TABLE_NAME + case SQL_DESC_BASE_TABLE_NAME: +#endif +#ifdef SQL_DESC_BASE_COLUMN_NAME + case SQL_DESC_BASE_COLUMN_NAME: +#endif + case SQL_DESC_TYPE_NAME: + if (val && valMax > 0) { + int vmax = valMax / sizeof (SQLWCHAR); + + v = uc_from_utf((SQLCHAR *) val, SQL_NTS); + if (v) { + uc_strncpy(val, v, vmax); + len = min(vmax, uc_strlen(v)); + uc_free(v); + len *= sizeof (SQLWCHAR); + } + if (vmax > 0) { + v = (SQLWCHAR *) val; + v[vmax - 1] = '\0'; + } + } + if (len <= 0) { + len = 0; + } + break; + } + if (valLen) { + *valLen = len; + } + } + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Internal return last HDBC or HSTMT error message. + * @param env environment handle or NULL + * @param dbc database connection handle or NULL + * @param stmt statement handle or NULL + * @param sqlState output buffer for SQL state + * @param nativeErr output buffer for native error code + * @param errmsg output buffer for error message + * @param errmax length of output buffer for error message + * @param errlen output length of error message + * @result ODBC error code + */ + +static SQLRETURN +drverror(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt, + SQLCHAR *sqlState, SQLINTEGER *nativeErr, + SQLCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen) +{ + SQLCHAR dummy0[6]; + SQLINTEGER dummy1; + SQLSMALLINT dummy2; + + if (env == SQL_NULL_HENV && + dbc == SQL_NULL_HDBC && + stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + if (sqlState) { + sqlState[0] = '\0'; + } else { + sqlState = dummy0; + } + if (!nativeErr) { + nativeErr = &dummy1; + } + *nativeErr = 0; + if (!errlen) { + errlen = &dummy2; + } + *errlen = 0; + if (errmsg) { + if (errmax > 0) { + errmsg[0] = '\0'; + } + } else { + errmsg = dummy0; + errmax = 0; + } + if (stmt) { + STMT *s = (STMT *) stmt; + + HSTMT_LOCK(stmt); + if (s->logmsg[0] == '\0') { + HSTMT_UNLOCK(stmt); + goto noerr; + } + *nativeErr = s->naterr; + strcpy((char *) sqlState, s->sqlstate); + if (errmax == SQL_NTS) { + strcpy((char *) errmsg, "[SQLite]"); + strcat((char *) errmsg, (char *) s->logmsg); + *errlen = strlen((char *) errmsg); + } else { + strncpy((char *) errmsg, "[SQLite]", errmax); + if (errmax - 8 > 0) { + strncpy((char *) errmsg + 8, (char *) s->logmsg, errmax - 8); + } + *errlen = min(strlen((char *) s->logmsg) + 8, errmax); + } + s->logmsg[0] = '\0'; + HSTMT_UNLOCK(stmt); + return SQL_SUCCESS; + } + if (dbc) { + DBC *d = (DBC *) dbc; + + HDBC_LOCK(dbc); + if (d->magic != DBC_MAGIC || d->logmsg[0] == '\0') { + HDBC_UNLOCK(dbc); + goto noerr; + } + *nativeErr = d->naterr; + strcpy((char *) sqlState, d->sqlstate); + if (errmax == SQL_NTS) { + strcpy((char *) errmsg, "[SQLite]"); + strcat((char *) errmsg, (char *) d->logmsg); + *errlen = strlen((char *) errmsg); + } else { + strncpy((char *) errmsg, "[SQLite]", errmax); + if (errmax - 8 > 0) { + strncpy((char *) errmsg + 8, (char *) d->logmsg, errmax - 8); + } + *errlen = min(strlen((char *) d->logmsg) + 8, errmax); + } + d->logmsg[0] = '\0'; + HDBC_UNLOCK(dbc); + return SQL_SUCCESS; + } +noerr: + sqlState[0] = '\0'; + errmsg[0] = '\0'; + *nativeErr = 0; + *errlen = 0; + return SQL_NO_DATA; +} + +#ifndef WINTERFACE +/** + * Return last HDBC or HSTMT error message. + * @param env environment handle or NULL + * @param dbc database connection handle or NULL + * @param stmt statement handle or NULL + * @param sqlState output buffer for SQL state + * @param nativeErr output buffer for native error code + * @param errmsg output buffer for error message + * @param errmax length of output buffer for error message + * @param errlen output length of error message + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLError(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt, + SQLCHAR *sqlState, SQLINTEGER *nativeErr, + SQLCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen) +{ + return drverror(env, dbc, stmt, sqlState, nativeErr, + errmsg, errmax, errlen); +} +#endif + +#ifdef WINTERFACE +/** + * Return last HDBC or HSTMT error message (UNICODE version). + * @param env environment handle or NULL + * @param dbc database connection handle or NULL + * @param stmt statement handle or NULL + * @param sqlState output buffer for SQL state + * @param nativeErr output buffer for native error code + * @param errmsg output buffer for error message + * @param errmax length of output buffer for error message + * @param errlen output length of error message + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLErrorW(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt, + SQLWCHAR *sqlState, SQLINTEGER *nativeErr, + SQLWCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen) +{ + char state[16]; + SQLSMALLINT len = 0; + SQLRETURN ret; + + ret = drverror(env, dbc, stmt, (SQLCHAR *) state, nativeErr, + (SQLCHAR *) errmsg, errmax, &len); + if (ret == SQL_SUCCESS) { + if (sqlState) { + uc_from_utf_buf((SQLCHAR *) state, -1, sqlState, + 6 * sizeof (SQLWCHAR)); + } + if (errmsg) { + if (len > 0) { + SQLWCHAR *e = NULL; + + e = uc_from_utf((SQLCHAR *) errmsg, len); + if (e) { + if (errmax > 0) { + uc_strncpy(errmsg, e, errmax); + e[len] = 0; + len = min(errmax, uc_strlen(e)); + } else { + len = uc_strlen(e); + } + uc_free(e); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + if (errmax > 0) { + errmsg[0] = 0; + } + } + } else { + len = 0; + } + if (errlen) { + *errlen = len; + } + } else if (ret == SQL_NO_DATA) { + if (sqlState) { + sqlState[0] = 0; + } + if (errmsg) { + if (errmax > 0) { + errmsg[0] = 0; + } + } + if (errlen) { + *errlen = 0; + } + } + return ret; +} +#endif + +/** + * Return information for more result sets. + * @param stmt statement handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLMoreResults(SQLHSTMT stmt) +{ + HSTMT_LOCK(stmt); + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + HSTMT_UNLOCK(stmt); + return SQL_NO_DATA; +} + +/** + * Internal function to setup column name/type information + * @param s statement poiner + * @param s4stmt sqlite4 statement pointer + * @param ncolsp pointer to preinitialized number of colums + * @result ODBC error code + */ + +static SQLRETURN +setupdyncols(STMT *s, sqlite4_stmt *s4stmt, int *ncolsp) +{ + int ncols = *ncolsp, guessed_types = 0; + SQLRETURN ret = SQL_SUCCESS; + + if (ncols > 0) { + int i; + PTRDIFF_T size; + char *p; + COL *dyncols; + DBC *d = (DBC *) s->dbc; + const char *colname, *typename; + char *tblname; + + for (i = size = 0; i < ncols; i++) { + colname = sqlite4_column_name(s4stmt, i); + size += 3 + 3 * strlen(colname); + } + tblname = (char *) size; + for (i = 0; i < ncols; i++) { + p = (char *) sqlite4_column_table_name(s4stmt, i); + size += 2 + (p ? strlen(p) : 0); + } + dyncols = xmalloc(ncols * sizeof (COL) + size); + if (!dyncols) { + freedyncols(s); + *ncolsp = 0; + ret = SQL_ERROR; + } else { + p = (char *) (dyncols + ncols); + tblname = p + (PTRDIFF_T) tblname; + for (i = 0; i < ncols; i++) { + char *q; + + colname = sqlite4_column_name(s4stmt, i); + if (d->trace) { + fprintf(d->trace, "-- column %d name: '%s'\n", + i + 1, colname); + fflush(d->trace); + } + q = (char *) sqlite4_column_table_name(s4stmt, i); + strcpy(tblname, q ? q : ""); + if (d->trace) { + fprintf(d->trace, "-- table %d name: '%s'\n", + i + 1, tblname); + fflush(d->trace); + } + dyncols[i].table = tblname; + tblname += strlen(tblname) + 1; + typename = s4stmt_coltype(s4stmt, i, d, &guessed_types); + dyncols[i].db = ((DBC *) (s->dbc))->dbname; + strcpy(p, colname); + dyncols[i].label = p; + p += strlen(p) + 1; + q = strchr(colname, '.'); + if (q) { + char *q2 = strchr(q + 1, '.'); + + /* SQLite 3.3.4 produces view.table.column sometimes */ + if (q2) { + q = q2; + } + } + if (q) { + dyncols[i].table = p; + strncpy(p, colname, q - colname); + p[q - colname] = '\0'; + p += strlen(p) + 1; + strcpy(p, q + 1); + dyncols[i].column = p; + p += strlen(p) + 1; + } else { + strcpy(p, colname); + dyncols[i].column = p; + p += strlen(p) + 1; + } + if (s->longnames) { + dyncols[i].column = dyncols[i].label; + } +#ifdef SQL_LONGVARCHAR + dyncols[i].type = SQL_LONGVARCHAR; + dyncols[i].size = 65535; +#else + dyncols[i].type = SQL_VARCHAR; + dyncols[i].size = 255; +#endif + dyncols[i].index = i; + dyncols[i].scale = 0; + dyncols[i].prec = 0; + dyncols[i].nosign = 1; + s4stmt_addmeta(s4stmt, i, d, &dyncols[i]); + dyncols[i].typename = xstrdup(typename); + } + freedyncols(s); + s->dyncols = s->cols = dyncols; + s->dcols = ncols; + fixupdyncols(s, d); + s->guessed_types = guessed_types; + } + } + return ret; +} + +/** + * Internal query preparation used by SQLPrepare() and SQLExecDirect(). + * @param stmt statement handle + * @param query query string + * @param queryLen length of query string or SQL_NTS + * @result ODBC error code + */ + +static SQLRETURN +drvprepare(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen) +{ + STMT *s; + DBC *d; + char *errp = NULL; + SQLRETURN sret; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (s->dbc == SQL_NULL_HDBC) { +noconn: + return noconn(s); + } + d = s->dbc; + if (!d->sqlite) { + goto noconn; + } + s4stmt_end(s); + s4stmt_drop(s); + sret = starttran(s); + if (sret != SQL_SUCCESS) { + return sret; + } + freep(&s->query); + s->query = (SQLCHAR *) fixupsql((char *) query, queryLen, + &s->nparams, &s->isselect, &errp); + if (!s->query) { + if (errp) { + setstat(s, -1, "%s", (*s->ov3) ? "HY000" : "S1000", errp); + return SQL_ERROR; + } + return nomem(s); + } + errp = NULL; + freeresult(s, -1); + if (s->isselect == 1) { + int ret, ncols, nretry = 0; + int sqlleft; + sqlite4_stmt *s4stmt = NULL; + + dbtraceapi(d, "sqlite4_prepare", (char *) s->query); + do { + s4stmt = NULL; + ret = sqlite4_prepare(d->sqlite, (char *) s->query, -1, + &s4stmt, &sqlleft); + if (ret != SQLITE4_OK) { + if (s4stmt) { + sqlite4_finalize(s4stmt); + s4stmt = NULL; + } + } + } while (ret == SQLITE4_SCHEMA && (++nretry) < 2); + dbtracerc(d, ret, NULL); + if (ret != SQLITE4_OK) { + if (s4stmt) { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s4stmt); + } + setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + sqlite4_errmsg(d->sqlite), ret); + return SQL_ERROR; + } + if (sqlite4_bind_parameter_count(s4stmt) != s->nparams) { + dbtraceapi(d, "sqlite4_finalize", 0); + sqlite4_finalize(s4stmt); + setstat(s, SQLITE4_ERROR, "parameter marker count incorrect", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + ncols = sqlite4_column_count(s4stmt); + s->guessed_types = 0; + setupdyncols(s, s4stmt, &ncols); + s->ncols = ncols; + s->s4stmt = s4stmt; + } + mkbindcols(s, s->ncols); + s->paramset_count = 0; + return SQL_SUCCESS; +} + +/** + * Internal query execution used by SQLExecute() and SQLExecDirect(). + * @param stmt statement handle + * @param initial false when called from SQLPutData() + * @result ODBC error code + */ + +static SQLRETURN +drvexecute(SQLHSTMT stmt, int initial) +{ + STMT *s; + DBC *d; + char *errp = NULL; + int rc, i, ncols = 0, nrows = 0; + SQLRETURN ret; + + if (stmt == SQL_NULL_HSTMT) { + return SQL_INVALID_HANDLE; + } + s = (STMT *) stmt; + if (s->dbc == SQL_NULL_HDBC) { +noconn: + return noconn(s); + } + d = (DBC *) s->dbc; + if (!d->sqlite) { + goto noconn; + } + if (!s->query) { + setstat(s, -1, "no query prepared", (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + if (s->nbindparms < s->nparams) { +unbound: + setstat(s, -1, "unbound parameters in query", + (*s->ov3) ? "HY000" : "S1000"); + return SQL_ERROR; + } + for (i = 0; i < s->nparams; i++) { + BINDPARM *p = &s->bindparms[i]; + + if (!p->bound) { + goto unbound; + } + if (initial) { + SQLLEN *lenp = p->lenp; + + if (lenp && *lenp < 0 && *lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *lenp != SQL_NTS && *lenp != SQL_NULL_DATA && + *lenp != SQL_DATA_AT_EXEC) { + setstat(s, -1, "invalid length reference", "HY009"); + return SQL_ERROR; + } + if (lenp && (*lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *lenp == SQL_DATA_AT_EXEC)) { + p->need = 1; + p->offs = 0; + p->len = 0; + } + } + } + ret = starttran(s); + if (ret != SQL_SUCCESS) { + goto cleanup; + } +again: + s4stmt_end(s); + if (initial) { + /* fixup data-at-execution parameters and alloc'ed blobs */ + s->pdcount = -1; + for (i = 0; i < s->nparams; i++) { + BINDPARM *p = &s->bindparms[i]; + + if (p->param == p->parbuf) { + p->param = NULL; + } + freep(&p->parbuf); + if (p->need <= 0 && + p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { + p->need = 1; + p->offs = 0; + p->len = 0; + } + } + } + if (s->nparams) { + for (i = 0; i < s->nparams; i++) { + ret = setupparam(s, (char *) s->query, i); + if (ret != SQL_SUCCESS) { + goto cleanup; + } + } + } + freeresult(s, 0); + if (s->isselect == 1 && !d->intrans && + s->curtype == SQL_CURSOR_FORWARD_ONLY && + d->step_enable && s->nparams == 0 && d->cur_s4stmt == NULL) { + s->nrows = -1; + ret = s4stmt_start(s); + if (ret == SQL_SUCCESS) { + goto done2; + } + } + rc = drvgettable(s, s->s4stmt ? NULL : (char *) s->query, &s->rows, + &s->nrows, &ncols, &errp, s->nparams, s->bindparms); + dbtracerc(d, rc, errp); + if (rc != SQLITE4_OK) { + setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", + errp ? errp : "unknown error", rc); + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + ret = SQL_ERROR; + goto cleanup; + } + if (errp) { + sqlite4_free(0, errp); + errp = NULL; + } + s->rowfree = freerows; + if (s->isselect <= 0 || s->isselect > 1) { + /* + * INSERT/UPDATE/DELETE or DDL results are immediately released. + */ + freeresult(s, -1); + nrows += sqlite4_changes(d->sqlite); + s->nrows = nrows; + goto done; + } + if (s->ncols != ncols) { + /* + * Weird result. + */ + setstat(s, -1, "broken result set %d/%d", + (*s->ov3) ? "HY000" : "S1000", s->ncols, ncols); + ret = SQL_ERROR; + goto cleanup; + } +done: + mkbindcols(s, s->ncols); +done2: + ret = SQL_SUCCESS; + s->rowp = s->rowprs = -1; + s->paramset_count++; + s->paramset_nrows = s->nrows; + if (s->paramset_count < s->paramset_size) { + for (i = 0; i < s->nparams; i++) { + BINDPARM *p = &s->bindparms[i]; + + if (p->param == p->parbuf) { + p->param = NULL; + } + freep(&p->parbuf); + if (p->lenp0 && + s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) { + p->lenp = (SQLLEN *) ((char *) p->lenp0 + + s->paramset_count * s->parm_bind_type); + } else if (p->lenp0 && p->inc > 0) { + p->lenp = p->lenp0 + s->paramset_count; + } + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { + if (p->param0 && + s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) { + p->param = (char *) p->param0 + + s->paramset_count * s->parm_bind_type; + } else if (p->param0 && p->inc > 0) { + p->param = (char *) p->param0 + + s->paramset_count * p->inc; + } + } else if (p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { + p->need = 1; + p->offs = 0; + p->len = 0; + } + } + goto again; + } +cleanup: + if (ret != SQL_NEED_DATA) { + for (i = 0; i < s->nparams; i++) { + BINDPARM *p = &s->bindparms[i]; + + if (p->param == p->parbuf) { + p->param = NULL; + } + freep(&p->parbuf); + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { + p->param = p->param0; + } + p->lenp = p->lenp0; + } + s->nrows = s->paramset_nrows; + if (s->parm_proc) { + *s->parm_proc = s->paramset_count; + } + s->paramset_count = 0; + s->paramset_nrows = 0; + } + /* + * For INSERT/UPDATE/DELETE statements change the return code + * to SQL_NO_DATA if the number of rows affected was 0. + */ + if (*s->ov3 && s->isselect == 0 && + ret == SQL_SUCCESS && nrows == 0) { + ret = SQL_NO_DATA; + } + return ret; +} + +#ifndef WINTERFACE +/** + * Prepare HSTMT. + * @param stmt statement handle + * @param query query string + * @param queryLen length of query string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLPrepare(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen) +{ + SQLRETURN ret; +#if defined(_WIN32) || defined(_WIN64) + char *q; +#endif + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvprepare(stmt, query, queryLen); + goto done; + } + q = wmb_to_utf_c((char *) query, queryLen); + if (!q) { + ret = nomem((STMT *) stmt); + goto done; + } + query = (SQLCHAR *) q; + queryLen = SQL_NTS; +#endif + ret = drvprepare(stmt, query, queryLen); +#if defined(_WIN32) || defined(_WIN64) + uc_free(q); +done: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Prepare HSTMT (UNICODE version). + * @param stmt statement handle + * @param query query string + * @param queryLen length of query string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLPrepareW(SQLHSTMT stmt, SQLWCHAR *query, SQLINTEGER queryLen) +{ + SQLRETURN ret; + char *q = uc_to_utf_c(query, queryLen); + + HSTMT_LOCK(stmt); + if (!q) { + ret = nomem((STMT *) stmt); + goto done; + } + ret = drvprepare(stmt, (SQLCHAR *) q, SQL_NTS); + uc_free(q); +done: + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +/** + * Execute query. + * @param stmt statement handle + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLExecute(SQLHSTMT stmt) +{ + SQLRETURN ret; + + HSTMT_LOCK(stmt); + ret = drvexecute(stmt, 1); + HSTMT_UNLOCK(stmt); + return ret; +} + +#ifndef WINTERFACE +/** + * Execute query directly. + * @param stmt statement handle + * @param query query string + * @param queryLen length of query string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLExecDirect(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen) +{ + SQLRETURN ret; +#if defined(_WIN32) || defined(_WIN64) + char *q; +#endif + + HSTMT_LOCK(stmt); +#if defined(_WIN32) || defined(_WIN64) + if (!((STMT *) stmt)->oemcp[0]) { + ret = drvprepare(stmt, query, queryLen); + if (ret == SQL_SUCCESS) { + ret = drvexecute(stmt, 1); + } + goto done; + } + q = wmb_to_utf_c((char *) query, queryLen); + if (!q) { + ret = nomem((STMT *) stmt); + goto done; + } + query = (SQLCHAR *) q; + queryLen = SQL_NTS; +#endif + ret = drvprepare(stmt, query, queryLen); + if (ret == SQL_SUCCESS) { + ret = drvexecute(stmt, 1); + } +#if defined(_WIN32) || defined(_WIN64) + uc_free(q); +done: + ; +#endif + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Execute query directly (UNICODE version). + * @param stmt statement handle + * @param query query string + * @param queryLen length of query string or SQL_NTS + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLExecDirectW(SQLHSTMT stmt, SQLWCHAR *query, SQLINTEGER queryLen) +{ + SQLRETURN ret; + char *q = uc_to_utf_c(query, queryLen); + + HSTMT_LOCK(stmt); + if (!q) { + ret = nomem((STMT *) stmt); + goto done; + } + ret = drvprepare(stmt, (SQLCHAR *) q, SQL_NTS); + uc_free(q); + if (ret == SQL_SUCCESS) { + ret = drvexecute(stmt, 1); + } +done: + HSTMT_UNLOCK(stmt); + return ret; +} +#endif + + +#if defined(_WIN32) || defined(_WIN64) +#ifndef WITHOUT_DRIVERMGR + +/* + * Windows configuration dialog stuff. + */ + +#include <windowsx.h> +#include <winuser.h> + +#define MAXPATHLEN (259+1) /* Max path length */ +#define MAXKEYLEN (15+1) /* Max keyword length */ +#define MAXDESC (255+1) /* Max description length */ +#define MAXDSNAME (32+1) /* Max data source name length */ +#define MAXTONAME (32+1) /* Max timeout length */ +#define MAXDBNAME MAXPATHLEN + +/* Attribute key indexes into an array of Attr structs, see below */ + +#define KEY_DSN 0 +#define KEY_DESC 1 +#define KEY_DBNAME 2 +#define KEY_BUSY 3 +#define KEY_DRIVER 4 +#define KEY_STEPAPI 5 +#define KEY_SYNCP 6 +#define KEY_NOTXN 7 +#define KEY_SHORTNAM 8 +#define KEY_LONGNAM 9 +#define KEY_NOCREAT 10 +#define KEY_NOWCHAR 11 +#define KEY_LOADEXT 12 +#define KEY_JMODE 13 +#define KEY_FKSUPPORT 14 +#define KEY_OEMCP 15 +#define KEY_BIGINT 16 +#define KEY_PASSWD 17 +#define NUMOFKEYS 18 + +typedef struct { + BOOL supplied; + char attr[MAXPATHLEN*4]; +} ATTR; + +typedef struct { + SQLHWND parent; + LPCSTR driver; + ATTR attr[NUMOFKEYS]; + char DSN[MAXDSNAME]; + BOOL newDSN; + BOOL defDSN; +} SETUPDLG; + +static struct { + char *key; + int ikey; +} attrLookup[] = { + { "DSN", KEY_DSN }, + { "DESC", KEY_DESC }, + { "Description", KEY_DESC}, + { "Database", KEY_DBNAME }, + { "Timeout", KEY_BUSY }, + { "Driver", KEY_DRIVER }, + { "StepAPI", KEY_STEPAPI }, + { "SyncPragma", KEY_SYNCP }, + { "NoTXN", KEY_NOTXN }, + { "ShortNames", KEY_SHORTNAM }, + { "LongNames", KEY_LONGNAM }, + { "NoCreat", KEY_NOCREAT }, + { "NoWCHAR", KEY_NOWCHAR }, + { "LoadExt", KEY_LOADEXT }, + { "JournalMode", KEY_JMODE }, + { "FKSupport", KEY_FKSUPPORT }, + { "OEMCP", KEY_OEMCP }, + { "BigInt", KEY_BIGINT }, + { "PWD", KEY_PASSWD }, + { NULL, 0 } +}; + +/** + * Setup dialog data from datasource attributes. + * @param attribs attribute string + * @param setupdlg pointer to dialog data + */ + +static void +ParseAttributes(LPCSTR attribs, SETUPDLG *setupdlg) +{ + char *str = (char *) attribs, *start, key[MAXKEYLEN]; + int elem, nkey; + + while (*str) { + start = str; + if ((str = strchr(str, '=')) == NULL) { + return; + } + elem = -1; + nkey = str - start; + if (nkey < sizeof (key)) { + int i; + + memcpy(key, start, nkey); + key[nkey] = '\0'; + for (i = 0; attrLookup[i].key; i++) { + if (strcasecmp(attrLookup[i].key, key) == 0) { + elem = attrLookup[i].ikey; + break; + } + } + } + start = ++str; + while (*str && *str != ';') { + ++str; + } + if (elem >= 0) { + int end = min(str - start, sizeof (setupdlg->attr[elem].attr) - 1); + + setupdlg->attr[elem].supplied = TRUE; + memcpy(setupdlg->attr[elem].attr, start, end); + setupdlg->attr[elem].attr[end] = '\0'; + } + ++str; + } +} + +/** + * Set datasource attributes in registry. + * @param parent handle of parent window + * @param setupdlg pointer to dialog data + * @result true or false + */ + +static BOOL +SetDSNAttributes(HWND parent, SETUPDLG *setupdlg) +{ + char *dsn = setupdlg->attr[KEY_DSN].attr; + + if (setupdlg->newDSN && strlen(dsn) == 0) { + return FALSE; + } + if (!SQLWriteDSNToIni(dsn, setupdlg->driver)) { + if (parent) { + char buf[MAXPATHLEN], msg[MAXPATHLEN]; + + LoadString(hModule, IDS_BADDSN, buf, sizeof (buf)); + wsprintf(msg, buf, dsn); + LoadString(hModule, IDS_MSGTITLE, buf, sizeof (buf)); + MessageBox(parent, msg, buf, + MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); + } + return FALSE; + } + if (parent || setupdlg->attr[KEY_DESC].supplied) { + SQLWritePrivateProfileString(dsn, "Description", + setupdlg->attr[KEY_DESC].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_DBNAME].supplied) { + SQLWritePrivateProfileString(dsn, "Database", + setupdlg->attr[KEY_DBNAME].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_BUSY].supplied) { + SQLWritePrivateProfileString(dsn, "Timeout", + setupdlg->attr[KEY_BUSY].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_STEPAPI].supplied) { + SQLWritePrivateProfileString(dsn, "StepAPI", + setupdlg->attr[KEY_STEPAPI].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_SYNCP].supplied) { + SQLWritePrivateProfileString(dsn, "SyncPragma", + setupdlg->attr[KEY_SYNCP].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_NOTXN].supplied) { + SQLWritePrivateProfileString(dsn, "NoTXN", + setupdlg->attr[KEY_NOTXN].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_SHORTNAM].supplied) { + SQLWritePrivateProfileString(dsn, "ShortNames", + setupdlg->attr[KEY_SHORTNAM].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_LONGNAM].supplied) { + SQLWritePrivateProfileString(dsn, "LongNames", + setupdlg->attr[KEY_LONGNAM].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_NOCREAT].supplied) { + SQLWritePrivateProfileString(dsn, "NoCreat", + setupdlg->attr[KEY_NOCREAT].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_NOWCHAR].supplied) { + SQLWritePrivateProfileString(dsn, "NoWCHAR", + setupdlg->attr[KEY_NOWCHAR].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_FKSUPPORT].supplied) { + SQLWritePrivateProfileString(dsn, "FKSupport", + setupdlg->attr[KEY_FKSUPPORT].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_OEMCP].supplied) { + SQLWritePrivateProfileString(dsn, "OEMCP", + setupdlg->attr[KEY_OEMCP].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_LOADEXT].supplied) { + SQLWritePrivateProfileString(dsn, "LoadExt", + setupdlg->attr[KEY_LOADEXT].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_BIGINT].supplied) { + SQLWritePrivateProfileString(dsn, "BigInt", + setupdlg->attr[KEY_BIGINT].attr, + ODBC_INI); + } + if (parent || setupdlg->attr[KEY_PASSWD].supplied) { + SQLWritePrivateProfileString(dsn, "PWD", + setupdlg->attr[KEY_PASSWD].attr, + ODBC_INI); + } + if (setupdlg->attr[KEY_DSN].supplied && + strcasecmp(setupdlg->DSN, setupdlg->attr[KEY_DSN].attr)) { + SQLRemoveDSNFromIni(setupdlg->DSN); + } + return TRUE; +} + +/** + * Get datasource attributes from registry. + * @param setupdlg pointer to dialog data + */ + +static void +GetAttributes(SETUPDLG *setupdlg) +{ + char *dsn = setupdlg->attr[KEY_DSN].attr; + + if (!setupdlg->attr[KEY_DESC].supplied) { + SQLGetPrivateProfileString(dsn, "Description", "", + setupdlg->attr[KEY_DESC].attr, + sizeof (setupdlg->attr[KEY_DESC].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_DBNAME].supplied) { + SQLGetPrivateProfileString(dsn, "Database", "", + setupdlg->attr[KEY_DBNAME].attr, + sizeof (setupdlg->attr[KEY_DBNAME].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_BUSY].supplied) { + SQLGetPrivateProfileString(dsn, "Timeout", "100000", + setupdlg->attr[KEY_BUSY].attr, + sizeof (setupdlg->attr[KEY_BUSY].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_STEPAPI].supplied) { + SQLGetPrivateProfileString(dsn, "StepAPI", "0", + setupdlg->attr[KEY_STEPAPI].attr, + sizeof (setupdlg->attr[KEY_STEPAPI].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_SYNCP].supplied) { + SQLGetPrivateProfileString(dsn, "SyncPragma", "NORMAL", + setupdlg->attr[KEY_SYNCP].attr, + sizeof (setupdlg->attr[KEY_SYNCP].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_NOTXN].supplied) { + SQLGetPrivateProfileString(dsn, "NoTXN", "", + setupdlg->attr[KEY_NOTXN].attr, + sizeof (setupdlg->attr[KEY_NOTXN].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_SHORTNAM].supplied) { + SQLGetPrivateProfileString(dsn, "ShortNames", "", + setupdlg->attr[KEY_SHORTNAM].attr, + sizeof (setupdlg->attr[KEY_SHORTNAM].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_LONGNAM].supplied) { + SQLGetPrivateProfileString(dsn, "LongNames", "", + setupdlg->attr[KEY_LONGNAM].attr, + sizeof (setupdlg->attr[KEY_LONGNAM].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_NOCREAT].supplied) { + SQLGetPrivateProfileString(dsn, "NoCreat", "", + setupdlg->attr[KEY_NOCREAT].attr, + sizeof (setupdlg->attr[KEY_NOCREAT].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_NOWCHAR].supplied) { + SQLGetPrivateProfileString(dsn, "NoWCHAR", "", + setupdlg->attr[KEY_NOWCHAR].attr, + sizeof (setupdlg->attr[KEY_NOWCHAR].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_FKSUPPORT].supplied) { + SQLGetPrivateProfileString(dsn, "FKSupport", "", + setupdlg->attr[KEY_FKSUPPORT].attr, + sizeof (setupdlg->attr[KEY_FKSUPPORT].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_OEMCP].supplied) { + SQLGetPrivateProfileString(dsn, "OEMCP", "", + setupdlg->attr[KEY_OEMCP].attr, + sizeof (setupdlg->attr[KEY_OEMCP].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_LOADEXT].supplied) { + SQLGetPrivateProfileString(dsn, "LoadExt", "", + setupdlg->attr[KEY_LOADEXT].attr, + sizeof (setupdlg->attr[KEY_LOADEXT].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_JMODE].supplied) { + SQLGetPrivateProfileString(dsn, "JournalMode", "", + setupdlg->attr[KEY_JMODE].attr, + sizeof (setupdlg->attr[KEY_JMODE].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_BIGINT].supplied) { + SQLGetPrivateProfileString(dsn, "BigInt", "", + setupdlg->attr[KEY_BIGINT].attr, + sizeof (setupdlg->attr[KEY_BIGINT].attr), + ODBC_INI); + } + if (!setupdlg->attr[KEY_PASSWD].supplied) { + SQLGetPrivateProfileString(dsn, "PWD", "", + setupdlg->attr[KEY_PASSWD].attr, + sizeof (setupdlg->attr[KEY_PASSWD].attr), + ODBC_INI); + } +} + +/** + * Open file dialog for selection of SQLite database file. + * @param hdlg handle of originating dialog window + */ + +static void +GetDBFile(HWND hdlg) +{ +#ifdef _WIN64 + SETUPDLG *setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER); +#else + SETUPDLG *setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER); +#endif + OPENFILENAME ofn; + + memset(&ofn, 0, sizeof (ofn)); + ofn.lStructSize = sizeof (ofn); + ofn.hwndOwner = hdlg; +#ifdef _WIN64 + ofn.hInstance = (HINSTANCE) GetWindowLongPtr(hdlg, GWLP_HINSTANCE); +#else + ofn.hInstance = (HINSTANCE) GetWindowLong(hdlg, GWL_HINSTANCE); +#endif + ofn.lpstrFile = (LPTSTR) setupdlg->attr[KEY_DBNAME].attr; + ofn.nMaxFile = MAXPATHLEN; + ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | + OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_FILEMUSTEXIST; + if (GetOpenFileName(&ofn)) { + SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr); + setupdlg->attr[KEY_DBNAME].supplied = TRUE; + } +} + +/** + * Dialog procedure for ConfigDSN(). + * @param hdlg handle of dialog window + * @param wmsg type of message + * @param wparam wparam of message + * @param lparam lparam of message + * @result true or false + */ + +static BOOL CALLBACK +ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) +{ + SETUPDLG *setupdlg = NULL; + WORD index; + + switch (wmsg) { + case WM_INITDIALOG: +#ifdef _WIN64 + SetWindowLong(hdlg, DWLP_USER, lparam); +#else + SetWindowLong(hdlg, DWL_USER, lparam); +#endif + setupdlg = (SETUPDLG *) lparam; + GetAttributes(setupdlg); + SetDlgItemText(hdlg, IDC_DSNAME, setupdlg->attr[KEY_DSN].attr); + SetDlgItemText(hdlg, IDC_DESC, setupdlg->attr[KEY_DESC].attr); + SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr); + SetDlgItemText(hdlg, IDC_TONAME, setupdlg->attr[KEY_BUSY].attr); + SetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].attr); + SendDlgItemMessage(hdlg, IDC_DSNAME, EM_LIMITTEXT, + (WPARAM) (MAXDSNAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_DESC, EM_LIMITTEXT, + (WPARAM) (MAXDESC - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_DBNAME, EM_LIMITTEXT, + (WPARAM) (MAXDBNAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_TONAME, EM_LIMITTEXT, + (WPARAM) (MAXTONAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_LOADEXT, EM_LIMITTEXT, + (WPARAM) (MAXPATHLEN*4 - 1), (LPARAM) 0); + CheckDlgButton(hdlg, IDC_STEPAPI, + getbool(setupdlg->attr[KEY_STEPAPI].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOTXN, + getbool(setupdlg->attr[KEY_NOTXN].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_SHORTNAM, + getbool(setupdlg->attr[KEY_SHORTNAM].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_LONGNAM, + getbool(setupdlg->attr[KEY_LONGNAM].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOCREAT, + getbool(setupdlg->attr[KEY_NOCREAT].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOWCHAR, + getbool(setupdlg->attr[KEY_NOWCHAR].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_FKSUPPORT, + getbool(setupdlg->attr[KEY_FKSUPPORT].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_OEMCP, + getbool(setupdlg->attr[KEY_OEMCP].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_BIGINT, + getbool(setupdlg->attr[KEY_BIGINT].attr) ? + BST_CHECKED : BST_UNCHECKED); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "NORMAL"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "OFF"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "FULL"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_SELECTSTRING, (WPARAM) -1, + (LPARAM) setupdlg->attr[KEY_SYNCP].attr); + if (setupdlg->defDSN) { + EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE); + EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE); + } + return TRUE; + case WM_COMMAND: + switch (GET_WM_COMMAND_ID(wparam, lparam)) { + case IDC_DSNAME: + if (GET_WM_COMMAND_CMD(wparam, lparam) == EN_CHANGE) { + char item[MAXDSNAME]; + + EnableWindow(GetDlgItem(hdlg, IDOK), + GetDlgItemText(hdlg, IDC_DSNAME, + item, sizeof (item))); + return TRUE; + } + break; + case IDC_BROWSE: + GetDBFile(hdlg); + break; + case IDOK: +#ifdef _WIN64 + setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER); +#else + setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER); +#endif + if (!setupdlg->defDSN) { + GetDlgItemText(hdlg, IDC_DSNAME, + setupdlg->attr[KEY_DSN].attr, + sizeof (setupdlg->attr[KEY_DSN].attr)); + } + GetDlgItemText(hdlg, IDC_DESC, + setupdlg->attr[KEY_DESC].attr, + sizeof (setupdlg->attr[KEY_DESC].attr)); + GetDlgItemText(hdlg, IDC_DBNAME, + setupdlg->attr[KEY_DBNAME].attr, + sizeof (setupdlg->attr[KEY_DBNAME].attr)); + GetDlgItemText(hdlg, IDC_TONAME, + setupdlg->attr[KEY_BUSY].attr, + sizeof (setupdlg->attr[KEY_BUSY].attr)); + GetDlgItemText(hdlg, IDC_LOADEXT, + setupdlg->attr[KEY_LOADEXT].attr, + sizeof (setupdlg->attr[KEY_LOADEXT].attr)); + index = SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); + if (index != (WORD) CB_ERR) { + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_GETLBTEXT, index, + (LPARAM) setupdlg->attr[KEY_SYNCP].attr); + } + strcpy(setupdlg->attr[KEY_STEPAPI].attr, + (IsDlgButtonChecked(hdlg, IDC_STEPAPI) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOTXN].attr, + (IsDlgButtonChecked(hdlg, IDC_NOTXN) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_SHORTNAM].attr, + (IsDlgButtonChecked(hdlg, IDC_SHORTNAM) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_LONGNAM].attr, + (IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOCREAT].attr, + (IsDlgButtonChecked(hdlg, IDC_NOCREAT) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOWCHAR].attr, + (IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_FKSUPPORT].attr, + (IsDlgButtonChecked(hdlg, IDC_FKSUPPORT) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_OEMCP].attr, + (IsDlgButtonChecked(hdlg, IDC_OEMCP) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_BIGINT].attr, + (IsDlgButtonChecked(hdlg, IDC_BIGINT) == BST_CHECKED) ? + "1" : "0"); + SetDSNAttributes(hdlg, setupdlg); + /* FALL THROUGH */ + case IDCANCEL: + EndDialog(hdlg, wparam); + return TRUE; + } + break; + } + return FALSE; +} + +/** + * ODBC INSTAPI procedure for DSN configuration. + * @param hwnd parent window handle + * @param request type of request + * @param driver driver name + * @param attribs attribute string of DSN + * @result true or false + */ + +BOOL INSTAPI +ConfigDSN(HWND hwnd, WORD request, LPCSTR driver, LPCSTR attribs) +{ + BOOL success; + SETUPDLG *setupdlg; + + setupdlg = (SETUPDLG *) xmalloc(sizeof (SETUPDLG)); + if (setupdlg == NULL) { + return FALSE; + } + memset(setupdlg, 0, sizeof (SETUPDLG)); + if (attribs) { + ParseAttributes(attribs, setupdlg); + } + if (setupdlg->attr[KEY_DSN].supplied) { + strcpy(setupdlg->DSN, setupdlg->attr[KEY_DSN].attr); + } else { + setupdlg->DSN[0] = '\0'; + } + if (request == ODBC_REMOVE_DSN) { + if (!setupdlg->attr[KEY_DSN].supplied) { + success = FALSE; + } else { + success = SQLRemoveDSNFromIni(setupdlg->attr[KEY_DSN].attr); + } + } else { + setupdlg->parent = hwnd; + setupdlg->driver = driver; + setupdlg->newDSN = request == ODBC_ADD_DSN; + setupdlg->defDSN = strcasecmp(setupdlg->attr[KEY_DSN].attr, + "Default") == 0; + if (hwnd) { + success = DialogBoxParam(hModule, MAKEINTRESOURCE(CONFIGDSN), + hwnd, (DLGPROC) ConfigDlgProc, + (LPARAM) setupdlg) == IDOK; + } else if (setupdlg->attr[KEY_DSN].supplied) { + success = SetDSNAttributes(hwnd, setupdlg); + } else { + success = FALSE; + } + } + xfree(setupdlg); + return success; +} + +/** + * Dialog procedure for SQLDriverConnect(). + * @param hdlg handle of dialog window + * @param wmsg type of message + * @param wparam wparam of message + * @param lparam lparam of message + * @result true or false + */ + +static BOOL CALLBACK +DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) +{ + SETUPDLG *setupdlg; + WORD index; + + switch (wmsg) { + case WM_INITDIALOG: +#ifdef _WIN64 + SetWindowLong(hdlg, DWLP_USER, lparam); +#else + SetWindowLong(hdlg, DWL_USER, lparam); +#endif + setupdlg = (SETUPDLG *) lparam; + SetDlgItemText(hdlg, IDC_DSNAME, setupdlg->attr[KEY_DSN].attr); + SetDlgItemText(hdlg, IDC_DESC, setupdlg->attr[KEY_DESC].attr); + SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr); + SetDlgItemText(hdlg, IDC_TONAME, setupdlg->attr[KEY_BUSY].attr); + SetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].attr); + SendDlgItemMessage(hdlg, IDC_DSNAME, EM_LIMITTEXT, + (WPARAM) (MAXDSNAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_DESC, EM_LIMITTEXT, + (WPARAM) (MAXDESC - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_DBNAME, EM_LIMITTEXT, + (WPARAM) (MAXDBNAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_TONAME, EM_LIMITTEXT, + (WPARAM) (MAXTONAME - 1), (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_LOADEXT, EM_LIMITTEXT, + (WPARAM) (MAXPATHLEN*4 - 1), (LPARAM) 0); + CheckDlgButton(hdlg, IDC_STEPAPI, + getbool(setupdlg->attr[KEY_STEPAPI].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOTXN, + getbool(setupdlg->attr[KEY_NOTXN].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_SHORTNAM, + getbool(setupdlg->attr[KEY_SHORTNAM].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_LONGNAM, + getbool(setupdlg->attr[KEY_LONGNAM].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOCREAT, + getbool(setupdlg->attr[KEY_NOCREAT].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_NOWCHAR, + getbool(setupdlg->attr[KEY_NOWCHAR].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_FKSUPPORT, + getbool(setupdlg->attr[KEY_FKSUPPORT].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_OEMCP, + getbool(setupdlg->attr[KEY_OEMCP].attr) ? + BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_BIGINT, + getbool(setupdlg->attr[KEY_BIGINT].attr) ? + BST_CHECKED : BST_UNCHECKED); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "NORMAL"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "OFF"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_ADDSTRING, 0, (LPARAM) "FULL"); + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_SELECTSTRING, (WORD) -1, + (LPARAM) setupdlg->attr[KEY_SYNCP].attr); + if (setupdlg->defDSN) { + EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE); + EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE); + } + return TRUE; + case WM_COMMAND: + switch (GET_WM_COMMAND_ID(wparam, lparam)) { + case IDC_BROWSE: + GetDBFile(hdlg); + break; + case IDOK: +#ifdef _WIN64 + setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER); +#else + setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER); +#endif + GetDlgItemText(hdlg, IDC_DSNAME, + setupdlg->attr[KEY_DSN].attr, + sizeof (setupdlg->attr[KEY_DSN].attr)); + GetDlgItemText(hdlg, IDC_DBNAME, + setupdlg->attr[KEY_DBNAME].attr, + sizeof (setupdlg->attr[KEY_DBNAME].attr)); + GetDlgItemText(hdlg, IDC_TONAME, + setupdlg->attr[KEY_BUSY].attr, + sizeof (setupdlg->attr[KEY_BUSY].attr)); + GetDlgItemText(hdlg, IDC_LOADEXT, + setupdlg->attr[KEY_LOADEXT].attr, + sizeof (setupdlg->attr[KEY_LOADEXT].attr)); + index = SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); + if (index != (WORD) CB_ERR) { + SendDlgItemMessage(hdlg, IDC_SYNCP, + CB_GETLBTEXT, index, + (LPARAM) setupdlg->attr[KEY_SYNCP].attr); + } + strcpy(setupdlg->attr[KEY_STEPAPI].attr, + (IsDlgButtonChecked(hdlg, IDC_STEPAPI) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOTXN].attr, + (IsDlgButtonChecked(hdlg, IDC_NOTXN) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_SHORTNAM].attr, + (IsDlgButtonChecked(hdlg, IDC_SHORTNAM) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_LONGNAM].attr, + (IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOCREAT].attr, + (IsDlgButtonChecked(hdlg, IDC_NOCREAT) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_NOWCHAR].attr, + (IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_FKSUPPORT].attr, + (IsDlgButtonChecked(hdlg, IDC_FKSUPPORT) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_OEMCP].attr, + (IsDlgButtonChecked(hdlg, IDC_OEMCP) == BST_CHECKED) ? + "1" : "0"); + strcpy(setupdlg->attr[KEY_BIGINT].attr, + (IsDlgButtonChecked(hdlg, IDC_BIGINT) == BST_CHECKED) ? + "1" : "0"); + /* FALL THROUGH */ + case IDCANCEL: + EndDialog(hdlg, GET_WM_COMMAND_ID(wparam, lparam) == IDOK); + return TRUE; + } + } + return FALSE; +} + +/** + * Internal connect using a driver connection string. + * @param dbc database connection handle + * @param hwnd parent window handle + * @param connIn driver connect input string + * @param connInLen length of driver connect input string or SQL_NTS + * @param connOut driver connect output string + * @param connOutMax length of driver connect output string + * @param connOutLen output length of driver connect output string + * @param drvcompl completion type + * @result ODBC error code + */ + +static SQLRETURN +drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, + SQLCHAR *connIn, SQLSMALLINT connInLen, + SQLCHAR *connOut, SQLSMALLINT connOutMax, + SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) +{ + BOOL maybeprompt, prompt = FALSE, defaultdsn = FALSE; + DBC *d; + SETUPDLG *setupdlg; + SQLRETURN ret; + char *dsn = NULL, *driver = NULL, *dbname = NULL; + + if (dbc == SQL_NULL_HDBC) { + return SQL_INVALID_HANDLE; + } + d = (DBC *) dbc; + if (d->sqlite) { + setstatd(d, -1, "connection already established", "08002"); + return SQL_ERROR; + } + setupdlg = (SETUPDLG *) xmalloc(sizeof (SETUPDLG)); + if (setupdlg == NULL) { + return SQL_ERROR; + } + memset(setupdlg, 0, sizeof (SETUPDLG)); + maybeprompt = drvcompl == SQL_DRIVER_COMPLETE || + drvcompl == SQL_DRIVER_COMPLETE_REQUIRED; + if (connIn == NULL || !connInLen || + (connInLen == SQL_NTS && !connIn[0])) { + prompt = TRUE; + } else { + ParseAttributes((LPCSTR) connIn, setupdlg); + if (!setupdlg->attr[KEY_DSN].attr[0] && + drvcompl == SQL_DRIVER_COMPLETE_REQUIRED) { + strcpy(setupdlg->attr[KEY_DSN].attr, "DEFAULT"); + defaultdsn = TRUE; + } + GetAttributes(setupdlg); + if (drvcompl == SQL_DRIVER_PROMPT || + (maybeprompt && + !setupdlg->attr[KEY_DBNAME].attr[0])) { + prompt = TRUE; + } + } +retry: + if (prompt) { + short dlgret; + + setupdlg->defDSN = setupdlg->attr[KEY_DRIVER].attr[0] != '\0'; + dlgret = DialogBoxParam(hModule, MAKEINTRESOURCE(DRIVERCONNECT), + hwnd, (DLGPROC) DriverConnectProc, + (LPARAM) setupdlg); + + if (!dlgret || dlgret == -1) { + xfree(setupdlg); + return SQL_NO_DATA; + } + } + dsn = setupdlg->attr[KEY_DSN].attr; + driver = setupdlg->attr[KEY_DRIVER].attr; + dbname = setupdlg->attr[KEY_DBNAME].attr; + if (connOut || connOutLen) { + char buf[SQL_MAX_MESSAGE_LENGTH * 4]; + int len, count; + char dsn_0 = (dsn && !defaultdsn) ? dsn[0] : '\0'; + char drv_0 = driver ? driver[0] : '\0'; + + buf[0] = '\0'; + count = snprintf(buf, sizeof (buf), + "%s%s%s%s%s%sDatabase=%s;StepAPI=%s;" + "SyncPragma=%s;NoTXN=%s;Timeout=%s;" + "ShortNames=%s;LongNames=%s;" + "NoCreat=%s;NoWCHAR=%s;" + "FKSupport=%s;JournalMode=%s;OEMCP=%s;LoadExt=%s;" + "BigInt=%s;PWD=%s", + dsn_0 ? "DSN=" : "", + dsn_0 ? dsn : "", + dsn_0 ? ";" : "", + drv_0 ? "Driver=" : "", + drv_0 ? driver : "", + drv_0 ? ";" : "", + dbname ? dbname : "", + setupdlg->attr[KEY_STEPAPI].attr, + setupdlg->attr[KEY_SYNCP].attr, + setupdlg->attr[KEY_NOTXN].attr, + setupdlg->attr[KEY_BUSY].attr, + setupdlg->attr[KEY_SHORTNAM].attr, + setupdlg->attr[KEY_LONGNAM].attr, + setupdlg->attr[KEY_NOCREAT].attr, + setupdlg->attr[KEY_NOWCHAR].attr, + setupdlg->attr[KEY_FKSUPPORT].attr, + setupdlg->attr[KEY_JMODE].attr, + setupdlg->attr[KEY_OEMCP].attr, + setupdlg->attr[KEY_LOADEXT].attr, + setupdlg->attr[KEY_BIGINT].attr, + setupdlg->attr[KEY_PASSWD].attr); + if (count < 0) { + buf[sizeof (buf) - 1] = '\0'; + } + len = min(connOutMax - 1, strlen(buf)); + if (connOut) { + strncpy((char *) connOut, buf, len); + connOut[len] = '\0'; + } + if (connOutLen) { + *connOutLen = len; + } + } + if (dsn[0]) { + char tracef[SQL_MAX_MESSAGE_LENGTH]; + + tracef[0] = '\0'; + SQLGetPrivateProfileString(setupdlg->attr[KEY_DSN].attr, + "tracefile", "", tracef, + sizeof (tracef), ODBC_INI); + if (tracef[0] != '\0') { + d->trace = fopen(tracef, "a"); + } + } + d->nowchar = getbool(setupdlg->attr[KEY_NOWCHAR].attr); + d->shortnames = getbool(setupdlg->attr[KEY_SHORTNAM].attr); + d->longnames = getbool(setupdlg->attr[KEY_LONGNAM].attr); + d->nocreat = getbool(setupdlg->attr[KEY_NOCREAT].attr); + d->fksupport = getbool(setupdlg->attr[KEY_FKSUPPORT].attr); + d->oemcp = getbool(setupdlg->attr[KEY_OEMCP].attr); + d->dobigint = getbool(setupdlg->attr[KEY_BIGINT].attr); + d->pwdLen = strlen(setupdlg->attr[KEY_PASSWD].attr); + d->pwd = (d->pwdLen > 0) ? setupdlg->attr[KEY_PASSWD].attr : NULL; + ret = dbopen(d, dbname ? dbname : "", 0, + dsn ? dsn : "", + setupdlg->attr[KEY_STEPAPI].attr, + setupdlg->attr[KEY_SYNCP].attr, + setupdlg->attr[KEY_NOTXN].attr, + setupdlg->attr[KEY_JMODE].attr, + setupdlg->attr[KEY_BUSY].attr); + if (ret != SQL_SUCCESS) { + if (maybeprompt && !prompt) { + prompt = TRUE; + goto retry; + } + } + memset(setupdlg->attr[KEY_PASSWD].attr, 0, + sizeof (setupdlg->attr[KEY_PASSWD].attr)); + if (ret == SQL_SUCCESS) { + dbloadext(d, setupdlg->attr[KEY_LOADEXT].attr); + } + xfree(setupdlg); + return ret; +} + +#endif /* WITHOUT_DRIVERMGR */ +#endif /* _WIN32 || _WIN64 */ + +#ifndef WINTERFACE +/** + * Connect using a driver connection string. + * @param dbc database connection handle + * @param hwnd parent window handle + * @param connIn driver connect input string + * @param connInLen length of driver connect input string or SQL_NTS + * @param connOut driver connect output string + * @param connOutMax length of driver connect output string + * @param connOutLen output length of driver connect output string + * @param drvcompl completion type + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDriverConnect(SQLHDBC dbc, SQLHWND hwnd, + SQLCHAR *connIn, SQLSMALLINT connInLen, + SQLCHAR *connOut, SQLSMALLINT connOutMax, + SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) +{ + SQLRETURN ret; + + HDBC_LOCK(dbc); + ret = drvdriverconnect(dbc, hwnd, connIn, connInLen, + connOut, connOutMax, connOutLen, drvcompl); + HDBC_UNLOCK(dbc); + return ret; +} +#endif + +#ifdef WINTERFACE +/** + * Connect using a driver connection string (UNICODE version). + * @param dbc database connection handle + * @param hwnd parent window handle + * @param connIn driver connect input string + * @param connInLen length of driver connect input string or SQL_NTS + * @param connOut driver connect output string + * @param connOutMax length of driver connect output string + * @param connOutLen output length of driver connect output string + * @param drvcompl completion type + * @result ODBC error code + */ + +SQLRETURN SQL_API +SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd, + SQLWCHAR *connIn, SQLSMALLINT connInLen, + SQLWCHAR *connOut, SQLSMALLINT connOutMax, + SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) +{ + SQLRETURN ret; + char *ci = NULL; + SQLSMALLINT len = 0; + + HDBC_LOCK(dbc); + if (connIn) { +#if defined(_WIN32) || defined(_WIN64) + if (connInLen == SQL_NTS) { + connInLen = -1; + } + ci = uc_to_wmb(connIn, connInLen); +#else + ci = uc_to_utf(connIn, connInLen); +#endif + if (!ci) { + DBC *d = (DBC *) dbc; + + setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000"); + HDBC_UNLOCK(dbc); + return SQL_ERROR; + } + } + ret = drvdriverconnect(dbc, hwnd, (SQLCHAR *) ci, SQL_NTS, + (SQLCHAR *) connOut, connOutMax, &len, drvcompl); + HDBC_UNLOCK(dbc); + uc_free(ci); + if (ret == SQL_SUCCESS) { + SQLWCHAR *co = NULL; + + if (connOut) { + if (len > 0) { +#if defined(_WIN32) || defined(_WIN64) + co = wmb_to_uc((char *) connOut, len); +#else + co = uc_from_utf((SQLCHAR *) connOut, len); +#endif + if (co) { + uc_strncpy(connOut, co, connOutMax / sizeof (SQLWCHAR)); + len = min(connOutMax / sizeof (SQLWCHAR), uc_strlen(co)); + uc_free(co); + } else { + len = 0; + } + } + if (len <= 0) { + len = 0; + connOut[0] = 0; + } + } else { + len = 0; + } + if (connOutLen) { + *connOutLen = len; + } + } + return ret; +} +#endif + +#if defined(_WIN32) || defined(_WIN64) + +/** + * DLL initializer for WIN32. + * @param hinst instance handle + * @param reason reason code for entry point + * @param reserved + * @result always true + */ + +BOOL APIENTRY +LibMain(HANDLE hinst, DWORD reason, LPVOID reserved) +{ + static int initialized = 0; + + switch (reason) { + case DLL_PROCESS_ATTACH: + if (!initialized++) { + hModule = hinst; +#ifdef WINTERFACE + /* MS Access hack part 1 (reserved error -7748) */ + statSpec2P = statSpec2; + statSpec3P = statSpec3; +#endif +#ifdef SQLITE_DYNLOAD + dls_init(); +#endif + } + break; + case DLL_THREAD_ATTACH: + break; + case DLL_PROCESS_DETACH: + if (--initialized <= 0) { +#ifdef SQLITE_DYNLOAD + dls_fini(); +#endif + } + break; + case DLL_THREAD_DETACH: + break; + default: + break; + } + return TRUE; +} + +/** + * DLL entry point for WIN32. + * @param hinst instance handle + * @param reason reason code for entry point + * @param reserved + * @result always true + */ + +int __stdcall +DllMain(HANDLE hinst, DWORD reason, LPVOID reserved) +{ + return LibMain(hinst, reason, reserved); +} + +#ifndef WITHOUT_INSTALLER + +/** + * Handler for driver installer/uninstaller error messages. + * @param name name of API function for which to show error messages + * @result true when error message retrieved + */ + +static BOOL +InUnError(char *name) +{ + WORD err = 1; + DWORD code; + char errmsg[301]; + WORD errlen, errmax = sizeof (errmsg) - 1; + int sqlret; + BOOL ret = FALSE; + + do { + errmsg[0] = '\0'; + sqlret = SQLInstallerError(err, &code, errmsg, errmax, &errlen); + if (SQL_SUCCEEDED(sqlret)) { + MessageBox(NULL, errmsg, name, + MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); + ret = TRUE; + } + err++; + } while (sqlret != SQL_NO_DATA); + return ret; +} + +/** + * Built in driver installer/uninstaller. + * @param remove true for uninstall + * @param cmdline command line string of rundll32 + */ + +static BOOL +InUn(int remove, char *cmdline) +{ +#ifdef SQLITE_HAS_CODEC + static char *drivername = "SQLite4 ODBC Driver (SEE)"; + static char *dsname = "SQLite4 SEE Datasource"; +#else + static char *drivername = "SQLite4 ODBC Driver"; + static char *dsname = "SQLite4 Datasource"; +#endif + char *dllname, *p; + char dllbuf[301], path[301], driver[300], attr[300], inst[400]; + WORD pathmax = sizeof (path) - 1, pathlen; + DWORD usecnt, mincnt; + int quiet = 0; + + dllbuf[0] = '\0'; + GetModuleFileName(hModule, dllbuf, sizeof (dllbuf)); + p = strrchr(dllbuf, '\\'); + dllname = p ? (p + 1) : dllbuf; + quiet = cmdline && strstr(cmdline, "quiet"); + if (SQLInstallDriverManager(path, pathmax, &pathlen)) { + sprintf(driver, "%s;Driver=%s;Setup=%s;", + drivername, dllname, dllname); + p = driver; + while (*p) { + if (*p == ';') { + *p = '\0'; + } + ++p; + } + usecnt = 0; + path[0] = '\0'; + SQLInstallDriverEx(driver, NULL, path, pathmax, NULL, + ODBC_INSTALL_INQUIRY, &usecnt); + pathlen = strlen(path); + while (pathlen > 0 && path[pathlen - 1] == '\\') { + --pathlen; + path[pathlen] = '\0'; + } + sprintf(driver, "%s;Driver=%s\\%s;Setup=%s\\%s;", + drivername, path, dllname, path, dllname); + p = driver; + while (*p) { + if (*p == ';') { + *p = '\0'; + } + ++p; + } + sprintf(inst, "%s\\%s", path, dllname); + if (!remove && usecnt > 0) { + /* first install try: copy over driver dll, keeping DSNs */ + if (GetFileAttributesA(dllbuf) != INVALID_FILE_ATTRIBUTES && + CopyFile(dllbuf, inst, 0)) { + if (!quiet) { + char buf[512]; + + sprintf(buf, "%s replaced.", drivername); + MessageBox(NULL, buf, "Info", + MB_ICONINFORMATION | MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); + } + return TRUE; + } + } + mincnt = remove ? 1 : 0; + while (usecnt != mincnt) { + if (!SQLRemoveDriver(driver, TRUE, &usecnt)) { + break; + } + } + if (remove) { + if (usecnt && !SQLRemoveDriver(driver, TRUE, &usecnt)) { + InUnError("SQLRemoveDriver"); + return FALSE; + } + if (!usecnt) { + char buf[512]; + + DeleteFile(inst); + if (!quiet) { + sprintf(buf, "%s uninstalled.", drivername); + MessageBox(NULL, buf, "Info", + MB_ICONINFORMATION |MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); + } + } + sprintf(attr, "DSN=%s;Database=sqlite.db;", dsname); + p = attr; + while (*p) { + if (*p == ';') { + *p = '\0'; + } + ++p; + } + SQLConfigDataSource(NULL, ODBC_REMOVE_SYS_DSN, drivername, attr); + return TRUE; + } + if (GetFileAttributesA(dllbuf) == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + if (strcasecmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) { + char buf[512]; + + sprintf(buf, "Copy %s to %s failed.", dllbuf, inst); + MessageBox(NULL, buf, "CopyFile", + MB_ICONSTOP |MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); + return FALSE; + } + if (!SQLInstallDriverEx(driver, path, path, pathmax, &pathlen, + ODBC_INSTALL_COMPLETE, &usecnt)) { + InUnError("SQLInstallDriverEx"); + return FALSE; + } + sprintf(attr, "DSN=%s;Database=sqlite.db;", dsname); + p = attr; + while (*p) { + if (*p == ';') { + *p = '\0'; + } + ++p; + } + SQLConfigDataSource(NULL, ODBC_REMOVE_SYS_DSN, drivername, attr); + if (!SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN, drivername, attr)) { + InUnError("SQLConfigDataSource"); + return FALSE; + } + if (!quiet) { + char buf[512]; + + sprintf(buf, "%s installed.", drivername); + MessageBox(NULL, buf, "Info", + MB_ICONINFORMATION | MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); + } + } else { + InUnError("SQLInstallDriverManager"); + return FALSE; + } + return TRUE; +} + +/** + * RunDLL32 entry point for driver installation. + * @param hwnd window handle of caller + * @param hinst of this DLL + * @param lpszCmdLine rundll32 command line tail + * @param nCmdShow ignored + */ + +void CALLBACK +install(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ + InUn(0, lpszCmdLine); +} + +/** + * RunDLL32 entry point for driver uninstallation. + * @param hwnd window handle of caller + * @param hinst of this DLL + * @param lpszCmdLine rundll32 command line tail + * @param nCmdShow ignored + */ + +void CALLBACK +uninstall(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ + InUn(1, lpszCmdLine); +} + +#endif /* WITHOUT_INSTALLER */ + +#ifndef WITHOUT_SHELL + +/** + * Setup argv vector from string + * @param argcp pointer to argc + * @param argvp pointer to argv + * @param cmdline command line string + * @param argv0 0th element for argv or NULL, must be static + */ + +static void +setargv(int *argcp, char ***argvp, char *cmdline, char *argv0) +{ + char *p, *arg, *argspace, **argv; + int argc, size, inquote, copy, slashes; + + size = 2 + (argv0 ? 1 : 0); + for (p = cmdline; *p != '\0'; p++) { + if (ISSPACE(*p)) { + size++; + while (ISSPACE(*p)) { + p++; + } + if (*p == '\0') { + break; + } + } + } + argspace = malloc(size * sizeof (char *) + strlen(cmdline) + 1); + argv = (char **) argspace; + argspace += size * sizeof (char *); + size--; + argc = 0; + if (argv0) { + argv[argc++] = argv0; + } + p = cmdline; + for (; argc < size; argc++) { + argv[argc] = arg = argspace; + while (ISSPACE(*p)) { + p++; + } + if (*p == '\0') { + break; + } + inquote = 0; + slashes = 0; + while (1) { + copy = 1; + while (*p == '\\') { + slashes++; + p++; + } + if (*p == '"') { + if ((slashes & 1) == 0) { + copy = 0; + if (inquote && p[1] == '"') { + p++; + copy = 1; + } else { + inquote = !inquote; + } + } + slashes >>= 1; + } + while (slashes) { + *arg = '\\'; + arg++; + slashes--; + } + if (*p == '\0' || (!inquote && ISSPACE(*p))) { + break; + } + if (copy != 0) { + *arg = *p; + arg++; + } + p++; + } + *arg = '\0'; + argspace = arg + 1; + } + argv[argc] = 0; + *argcp = argc; + *argvp = argv; +} + +/** + * RunDLL32 entry point for SQLite shell + * @param hwnd window handle of caller + * @param hinst of this DLL + * @param lpszCmdLine rundll32 command line tail + * @param nCmdShow ignored + */ + +void CALLBACK +shell(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ + int argc, needcon = 0; + char **argv; + extern int sqlite4_main(int, char **); + static const char *name = "SQLite4 Shell"; + DWORD ftype0, ftype1, ftype2; + + ftype0 = GetFileType(GetStdHandle(STD_INPUT_HANDLE)); + ftype1 = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); + ftype2 = GetFileType(GetStdHandle(STD_ERROR_HANDLE)); + if (ftype0 != FILE_TYPE_DISK && ftype0 != FILE_TYPE_CHAR && + ftype0 != FILE_TYPE_PIPE) { + fclose(stdin); + ++needcon; + ftype0 = FILE_TYPE_UNKNOWN; + } + if (ftype1 != FILE_TYPE_DISK && ftype1 != FILE_TYPE_CHAR && + ftype1 != FILE_TYPE_PIPE) { + fclose(stdout); + ++needcon; + ftype1 = FILE_TYPE_UNKNOWN; + } + if (ftype2 != FILE_TYPE_DISK && ftype2 != FILE_TYPE_CHAR && + ftype2 != FILE_TYPE_PIPE) { + fclose(stderr); + ++needcon; + ftype2 = FILE_TYPE_UNKNOWN; + } + if (needcon > 0) { + AllocConsole(); + SetConsoleTitle(name); + } + if (ftype0 == FILE_TYPE_UNKNOWN) { + freopen("CONIN$", "r", stdin); + } + if (ftype1 == FILE_TYPE_UNKNOWN) { + freopen("CONOUT$", "w", stdout); + } + if (ftype2 == FILE_TYPE_UNKNOWN) { + freopen("CONOUT$", "w", stderr); + } + setargv(&argc, &argv, lpszCmdLine, (char *) name); + sqlite4_main(argc, argv); +} + +#endif /* WITHOUT_SHELL */ + +#endif /* _WIN32 || _WIN64 */ + +#if defined(HAVE_ODBCINSTEXT_H) && (HAVE_ODBCINSTEXT_H) + +/* + * unixODBC property page for this driver, + * may or may not work depending on unixODBC version. + */ + +#include <odbcinstext.h> + +int +ODBCINSTGetProperties(HODBCINSTPROPERTY prop) +{ + static const char *instYN[] = { "No", "Yes", NULL }; + static const char *syncPragma[] = { "NORMAL", "OFF", "FULL", NULL }; + static const char *jmPragma[] = { + "DELETE", "PERSIST", "OFF", "TRUNCATE", "MEMORY", "WAL", NULL + }; + + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_FILENAME; + strncpy(prop->szName, "Database", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_TEXTEDIT; + strncpy(prop->szName, "Timeout", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "100000", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "StepAPI", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "ShortNames", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "LongNames", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "NoCreat", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); +#ifdef WINTERFACE + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "NoWCHAR", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); +#endif + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "FKSupport", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (syncPragma)); + memcpy(prop->aPromptData, syncPragma, sizeof (syncPragma)); + strncpy(prop->szName, "SyncPragma", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "NORMAL", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (jmPragma)); + memcpy(prop->aPromptData, jmPragma, sizeof (jmPragma)); + strncpy(prop->szName, "JournalMode", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "DELETE", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + prop = prop->pNext; + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_TEXTEDIT; + strncpy(prop->szName, "LoadExt", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "", INI_MAX_PROPERTY_VALUE); + prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); + memset(prop, 0, sizeof (ODBCINSTPROPERTY)); + prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; + prop->aPromptData = malloc(sizeof (instYN)); + memcpy(prop->aPromptData, instYN, sizeof (instYN)); + strncpy(prop->szName, "BigInt", INI_MAX_PROPERTY_NAME); + strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); + return 1; +} + +#endif /* HAVE_ODBCINSTEXT_H */ + +#ifdef SQLITE_DYNLOAD + +/* + * SQLite4 shared library/DLL stubs. + */ + +static void +dls_void(void) +{ +} + +static int +dls_error(void) +{ + return SQLITE4_ERROR; +} + +static int +dls_0(void) +{ + return 0; +} + +#if 0 +static sqlite4_int64 +dls_0LL(void) +{ + return 0; +} +#endif + +static double +dls_00(void) +{ + return 0; +} + +static void * +dls_null(void) +{ + return NULL; +} + +static const char * +dls_empty(void) +{ + return ""; +} + +static int +dls_snull(void) +{ + return SQLITE4_NULL; +} + +#define DLS_ENT(name, func) \ + { "sqlite4_" #name, offsetof(struct dl_sqlite4_funcs, name), \ + (void *) func } + +#define DLS_ENT3(name, off, func) \ + { "sqlite4_" #name, offsetof(struct dl_sqlite4_funcs, off), \ + (void *) func } + +#define DLS_END { NULL, 0, NULL } + +static struct { + const char *name; + int offset; + void *func; +} dls_nametab[] = { + DLS_ENT(bind_blob, dls_error), + DLS_ENT(bind_double, dls_error), + DLS_ENT(bind_int, dls_error), + DLS_ENT(bind_int64, dls_error), + DLS_ENT(bind_null, dls_error), + DLS_ENT(bind_parameter_count, dls_0), + DLS_ENT(bind_text, dls_error), + DLS_ENT(changes, dls_0), + DLS_ENT(close, dls_error), + DLS_ENT(column_blob, dls_null), + DLS_ENT(column_bytes, dls_0), + DLS_ENT(column_count, dls_0), + DLS_ENT(column_database_name, dls_empty), + DLS_ENT(column_decltype, dls_empty), + DLS_ENT(column_double, dls_00), + DLS_ENT(column_name, dls_empty), + DLS_ENT(column_origin_name, dls_null), + DLS_ENT(column_table_name, dls_null), + DLS_ENT(column_text, dls_null), + DLS_ENT(column_type, dls_snull), + DLS_ENT(create_function, dls_error), + DLS_ENT(errcode, dls_error), + DLS_ENT(errmsg, dls_empty), + DLS_ENT(exec, dls_error), + DLS_ENT(finalize, dls_error), + DLS_ENT(free, free), + DLS_ENT(interrupt, dls_void), +#if 0 + DLS_ENT(last_insert_rowid, dls_0LL), +#endif + DLS_ENT(libversion, dls_empty), +#if 0 + DLS_ENT(load_extension, dls_error), +#endif + DLS_ENT(malloc, malloc), + DLS_ENT(mprintf, dls_null), + DLS_ENT(open, dls_error), + DLS_ENT(prepare, dls_error), + DLS_ENT(profile, dls_null), + DLS_ENT(realloc, realloc), + DLS_ENT(reset, dls_error), + DLS_ENT(result_blob, dls_void), + DLS_ENT(result_error, dls_void), + DLS_ENT(result_int, dls_void), + DLS_ENT(result_null, dls_void), + DLS_ENT(step, dls_error), +#if defined(_WIN32) || defined(_WIN64) + DLS_ENT3(strnicmp, xstrnicmp, _strnicmp), +#else + DLS_ENT3(strnicmp, xstrnicmp, strncasecmp), +#endif +#if 0 + DLS_ENT(table_column_metadata, dls_error), +#endif + DLS_ENT(trace, dls_null), + DLS_ENT(user_data, dls_null), + DLS_ENT(value_blob, dls_null), + DLS_ENT(value_bytes, dls_0), + DLS_ENT(value_text, dls_empty), + DLS_ENT(value_type, dls_snull), + DLS_END +}; + +#if defined(_WIN32) || defined(_WIN64) + +static HMODULE sqlite4_dll = 0; + +static void +dls_init(void) +{ + int i; + static const char *dll_names[] = { + "System.Data.SQLite.dll", + "sqlite4.dll", + NULL, + }; + + i = 0; + while (dll_names[i]) { + sqlite4_dll = LoadLibrary(dll_names[i]); + if (sqlite4_dll) { + break; + } + ++i; + } + i = 0; + while (dls_nametab[i].name) { + void *func = 0, **loc; + + if (sqlite4_dll) { + func = (void *) GetProcAddress(sqlite4_dll, dls_nametab[i].name); + } + if (!func) { + func = dls_nametab[i].func; + } + loc = (void **) ((char *) &dls_funcs + dls_nametab[i].offset); + *loc = func; + ++i; + } + if (!sqlite4_dll) { + char buf[MAXPATHLEN], msg[MAXPATHLEN]; + + LoadString(hModule, IDS_DRVTITLE, buf, sizeof (buf)); + LoadString(hModule, IDS_DLLERR, msg, sizeof (msg)); + MessageBox(NULL, msg, buf, + MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL | + MB_SETFOREGROUND); + } +} + +static void +dls_fini(void) +{ + if (sqlite4_dll) { + FreeLibrary(sqlite4_dll); + sqlite4_dll = 0; + } +} + +#else + +#include <dlfcn.h> + +static void *libsqlite4_so = 0; + +void +dls_init(void) +{ + int i; + + libsqlite4_so = dlopen("libsqlite4.so.0", RTLD_NOW | RTLD_GLOBAL); + i = 0; + while (dls_nametab[i].name) { + void *func = 0, **loc; + + if (libsqlite4_so) { + func = dlsym(libsqlite4_so, dls_nametab[i].name); + } + if (!func) { + func = dls_nametab[i].func; + } + loc = (void **) ((char *) &dls_funcs + dls_nametab[i].offset); + *loc = func; + ++i; + } + if (!libsqlite4_so) { + const char errmsg[] = "sqlite4 shared library not found.\n"; + + write(2, errmsg, sizeof (errmsg) - 1); + } +} + +void +dls_fini(void) +{ + if (libsqlite4_so) { + dlclose(libsqlite4_so); + libsqlite4_so = 0; + } +} + +#endif + +#endif diff --git a/lang/sql/odbc/sqlite4odbc.h b/lang/sql/odbc/sqlite4odbc.h new file mode 100644 index 00000000..e936fb6a --- /dev/null +++ b/lang/sql/odbc/sqlite4odbc.h @@ -0,0 +1,292 @@ +#ifndef _SQLITE4ODBC_H +#define _SQLITE4ODBC_H + +/** + * @mainpage + * @section readme README + * @verbinclude README + * @section changelog ChangeLog + * @verbinclude ChangeLog + * @section copying License Terms + * @verbinclude license.terms + */ + +/** + * @file sqlite4odbc.h + * Header file for SQLite4 ODBC driver. + * + * $Id: sqlite4odbc.h,v 1.2 2013/09/23 09:20:26 chw Exp chw $ + * + * Copyright (c) 2013 Christian Werner <chw@ch-werner.de> + * + * See the file "license.terms" for information on usage + * and redistribution of this file and for a + * DISCLAIMER OF ALL WARRANTIES. + */ + +#if defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#include <stdio.h> +#include <io.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#endif +#include <stdlib.h> +#if defined(HAVE_LOCALECONV) || defined(_WIN32) || defined(_WIN64) +#include <locale.h> +#endif +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <sql.h> +#include <sqlext.h> +#include <time.h> + +#include "sqlite4.h" +#ifdef HAVE_IODBC +#include <iodbcinst.h> +#endif +#if defined(HAVE_UNIXODBC) || defined(_WIN32) || defined(_WIN64) +#include <odbcinst.h> +#endif + +#ifndef SQL_API +#define SQL_API +#endif + +#ifndef HAVE_SQLLEN +#define SQLLEN SQLINTEGER +#endif + +#define SQLLEN_PTR SQLLEN * + +#ifndef HAVE_SQLULEN +#define SQLULEN SQLUINTEGER +#endif + +#ifndef HAVE_SQLROWCOUNT +#define SQLROWCOUNT SQLUINTEGER +#endif + +#ifndef HAVE_SQLSETPOSIROW +#define SQLSETPOSIROW SQLUSMALLINT +#endif + +#ifndef HAVE_SQLROWOFFSET +#define SQLROWOFFSET SQLLEN +#endif + +#ifndef HAVE_SQLROWSETSIZE +#define SQLROWSETSIZE SQLULEN +#endif + +struct dbc; +struct stmt; + +/** + * @typedef ENV + * @struct ENV + * Driver internal structure for environment (HENV). + */ + +typedef struct { + int magic; /**< Magic cookie */ + int ov3; /**< True for SQL_OV_ODBC3 */ +#if defined(_WIN32) || defined(_WIN64) + CRITICAL_SECTION cs; /**< For serializing most APIs */ + DWORD owner; /**< Current owner of CS or 0 */ +#endif + struct dbc *dbcs; /**< Pointer to first DBC */ +} ENV; + +/** + * @typedef DBC + * @struct dbc + * Driver internal structure for database connection (HDBC). + */ + +typedef struct dbc { + int magic; /**< Magic cookie */ + ENV *env; /**< Pointer to environment */ + struct dbc *next; /**< Pointer to next DBC */ + sqlite4 *sqlite; /**< SQLITE database handle */ + int version; /**< SQLITE version number */ + char *dbname; /**< SQLITE database name */ + char *dsn; /**< ODBC data source name */ + int timeout; /**< Lock timeout value */ + long t0; /**< Start time for SQLITE busy handler */ + int busyint; /**< Interrupt busy handler from SQLCancel() */ + int *ov3; /**< True for SQL_OV_ODBC3 */ + int ov3val; /**< True for SQL_OV_ODBC3 */ + int autocommit; /**< Auto commit state */ + int intrans; /**< True when transaction started */ + struct stmt *stmt; /**< STMT list of this DBC */ + int naterr; /**< Native error code */ + char sqlstate[6]; /**< SQL state for SQLError() */ + SQLCHAR logmsg[1024]; /**< Message for SQLError() */ + int nowchar; /**< Don't try to use WCHAR */ + int dobigint; /**< Force SQL_BIGINT for INTEGER columns */ + int shortnames; /**< Always use short column names */ + int longnames; /**< Don't shorten column names */ + int nocreat; /**< Don't auto create database file */ + int fksupport; /**< Foreign keys on or off */ + int curtype; /**< Default cursor type */ + int step_enable; /**< True for sqlite_compile/step/finalize */ + int trans_disable; /**< True for no transaction support */ + int oemcp; /**< True for Win32 OEM CP translation */ + struct stmt *cur_s4stmt; /**< Current STMT executing sqlite statement */ + int s4stmt_needmeta; /**< True to get meta data in s4stmt_step(). */ + FILE *trace; /**< sqlite4_trace() file pointer or NULL */ + char *pwd; /**< Password or NULL */ + int pwdLen; /**< Length of password */ +#ifdef USE_DLOPEN_FOR_GPPS + void *instlib; + int (*gpps)(); +#endif +#if defined(_WIN32) || defined(_WIN64) + int xcelqrx; +#endif +} DBC; + +/** + * @typedef COL + * @struct COL + * Internal structure to describe a column in a result set. + */ + +typedef struct { + char *db; /**< Database name */ + char *table; /**< Table name */ + char *column; /**< Column name */ + int type; /**< Data type of column */ + int size; /**< Size of column */ + int index; /**< Index of column in result */ + int nosign; /**< Unsigned type */ + int scale; /**< Scale of column */ + int prec; /**< Precision of column */ + int autoinc; /**< AUTO_INCREMENT column */ + int notnull; /**< NOT NULL constraint on column */ + int ispk; /**< Flag for primary key (> 0) */ + int isrowid; /**< Flag for ROWID column (> 0) */ + char *typename; /**< Column type name or NULL */ + char *label; /**< Column label or NULL */ +} COL; + +/** + * @typedef BINDCOL + * @struct BINDCOL + * Internal structure for bound column (SQLBindCol). + */ + +typedef struct { + SQLSMALLINT type; /**< ODBC type */ + SQLINTEGER max; /**< Max. size of value buffer */ + SQLLEN *lenp; /**< Value return, actual size of value buffer */ + SQLPOINTER valp; /**< Value buffer */ + int index; /**< Index of column in result */ + int offs; /**< Byte offset for SQLGetData() */ +} BINDCOL; + +/** + * @typedef BINDPARM + * @struct BINDPARM + * Internal structure for bound parameter (SQLBindParameter). + */ + +typedef struct { + int type, stype; /**< ODBC and SQL types */ + int coldef, scale; /**< from SQLBindParameter() */ + SQLLEN max; /**< Max. size size of parameter buffer */ + SQLLEN *lenp; /**< Actual size of parameter buffer */ + SQLLEN *lenp0; /**< Actual size of parameter buffer, initial value */ + void *param; /**< Parameter buffer */ + void *param0; /**< Parameter buffer, initial value */ + int inc; /**< Increment for paramset size > 1 */ + int need; /**< True when SQL_LEN_DATA_AT_EXEC */ + int bound; /**< True when SQLBindParameter() called */ + int offs, len; /**< Offset/length for SQLParamData()/SQLPutData() */ + void *parbuf; /**< Buffer for SQL_LEN_DATA_AT_EXEC etc. */ + char strbuf[64]; /**< String buffer for scalar data */ + int s4type; /**< SQLite4 type */ + int s4size; /**< SQLite4 size */ + void *s4val; /**< SQLite4 value buffer */ + int s4ival; /**< SQLite4 integer value */ + sqlite4_int64 s4lival; /**< SQLite4 64bit integer value */ + double s4dval; /**< SQLite4 float value */ +} BINDPARM; + +/** + * @typedef STMT + * @struct stmt + * Driver internal structure representing SQL statement (HSTMT). + */ + +typedef struct stmt { + struct stmt *next; /**< Linkage for STMT list in DBC */ + HDBC dbc; /**< Pointer to DBC */ + SQLCHAR cursorname[32]; /**< Cursor name */ + SQLCHAR *query; /**< Current query, raw string */ + int *ov3; /**< True for SQL_OV_ODBC3 */ + int *oemcp; /**< True for Win32 OEM CP translation */ + int isselect; /**< > 0 if query is a SELECT statement */ + int ncols; /**< Number of result columns */ + COL *cols; /**< Result column array */ + COL *dyncols; /**< Column array, but malloc()ed */ + int dcols; /**< Number of entries in dyncols */ + int bkmrk; /**< True when bookmarks used */ + SQLINTEGER *bkmrkptr; /**< SQL_ATTR_FETCH_BOOKMARK_PTR */ + BINDCOL bkmrkcol; /**< Bookmark bound column */ + BINDCOL *bindcols; /**< Array of bound columns */ + int nbindcols; /**< Number of entries in bindcols */ + int nbindparms; /**< Number bound parameters */ + BINDPARM *bindparms; /**< Array of bound parameters */ + int nparams; /**< Number of parameters in query */ + int pdcount; /**< SQLParamData() counter */ + int nrows; /**< Number of result rows */ + int rowp; /**< Current result row */ + int rowprs; /**< Current start row of rowset */ + char **rows; /**< 2-dim array, result set */ + void (*rowfree)(); /**< Free function for rows */ + int naterr; /**< Native error code */ + char sqlstate[6]; /**< SQL state for SQLError() */ + SQLCHAR logmsg[1024]; /**< Message for SQLError() */ + int nowchar[2]; /**< Don't try to use WCHAR */ + int dobigint; /**< Force SQL_BIGINT for INTEGER columns */ + int longnames; /**< Don't shorten column names */ + SQLULEN retr_data; /**< SQL_ATTR_RETRIEVE_DATA */ + SQLULEN rowset_size; /**< Size of rowset */ + SQLUSMALLINT *row_status; /**< Row status pointer */ + SQLUSMALLINT *row_status0; /**< Internal status array */ + SQLUSMALLINT row_status1; /**< Internal status array for 1 row rowsets */ + SQLULEN *row_count; /**< Row count pointer */ + SQLULEN row_count0; /**< Row count */ + SQLULEN paramset_size; /**< SQL_ATTR_PARAMSET_SIZE */ + SQLULEN paramset_count; /**< Internal for paramset */ + SQLUINTEGER paramset_nrows; /**< Row count for paramset handling */ + SQLULEN max_rows; /**< SQL_ATTR_MAX_ROWS */ + SQLULEN bind_type; /**< SQL_ATTR_ROW_BIND_TYPE */ + SQLULEN *bind_offs; /**< SQL_ATTR_ROW_BIND_OFFSET_PTR */ + /* Dummies to make ADO happy */ + SQLULEN *parm_bind_offs; /**< SQL_ATTR_PARAM_BIND_OFFSET_PTR */ + SQLUSMALLINT *parm_oper; /**< SQL_ATTR_PARAM_OPERATION_PTR */ + SQLUSMALLINT *parm_status; /**< SQL_ATTR_PARAMS_STATUS_PTR */ + SQLULEN *parm_proc; /**< SQL_ATTR_PARAMS_PROCESSED_PTR */ + SQLULEN parm_bind_type; /**< SQL_ATTR_PARAM_BIND_TYPE */ + int curtype; /**< Cursor type */ + sqlite4_stmt *s4stmt; /**< SQLite statement handle or NULL */ + int s4stmt_noreset; /**< False when sqlite4_reset() needed. */ + int s4stmt_rownum; /**< Current row number */ + char *bincell; /**< Cache for blob data */ + char *bincache; /**< Cache for blob data */ + int binlen; /**< Length of blob data */ + int guessed_types; /**< Flag for drvprepare()/drvexecute() */ + int one_tbl; /**< Flag for single table (> 0) */ + int has_pk; /**< Flag for primary key (> 0) */ + int has_rowid; /**< Flag for ROWID (>= 0 or -1) */ +} STMT; + +#endif diff --git a/lang/sql/odbc/sqlite4odbc.rc b/lang/sql/odbc/sqlite4odbc.rc new file mode 100644 index 00000000..c8fab1d7 --- /dev/null +++ b/lang/sql/odbc/sqlite4odbc.rc @@ -0,0 +1,154 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite4.h" + +#ifndef SQLITE4_VERSION +#define SQLITE4_VERSION "?.?.?" +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +DRIVERCONNECT DIALOG DISCARDABLE 65, 43, 277, 175 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "SQLite4 ODBC Driver Connect" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_DSNAME,82,20,140,12,ES_AUTOHSCROLL | WS_GROUP + EDITTEXT IDC_DBNAME,82,36,140,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,225,35,40,14 + EDITTEXT IDC_TONAME,82,51,53,12,ES_AUTOHSCROLL + CONTROL "No TXN",IDC_NOTXN,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,50,52,15 + CONTROL "Step API",IDC_STEPAPI,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,225,50,52,15 + COMBOBOX IDC_SYNCP,82,67,69,52,CBS_DROPDOWN | CBS_HASSTRINGS | + WS_VSCROLL | WS_TABSTOP + CONTROL "Short Column Names",IDC_SHORTNAM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,170,65,95,15 + CONTROL "Foreign Keys",IDC_FKSUPPORT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,80,75,15 + CONTROL "Long Column Names",IDC_LONGNAM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,80,95,15 + CONTROL "OEMCP Translation",IDC_OEMCP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,95,75,15 + CONTROL "Don't Create Database",IDC_NOCREAT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,170,95,95,15 + CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,110,75,15 + CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,110,52,15 + EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,150,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + CTEXT "Enter options for connect",1003,95,6,80,8 + RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP + RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP + RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP + RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP +END + +CONFIGDSN DIALOG DISCARDABLE 65, 43, 277, 175 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "SQLite4 ODBC DSN Configuration" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_DSNAME,82,20,140,12,ES_AUTOHSCROLL | WS_GROUP + EDITTEXT IDC_DBNAME,82,36,140,12,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE,225,35,40,14 + EDITTEXT IDC_TONAME,82,51,53,12,ES_AUTOHSCROLL + CONTROL "No TXN",IDC_NOTXN,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,50,52,15 + CONTROL "Step API",IDC_STEPAPI,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,225,50,52,15 + COMBOBOX IDC_SYNCP,82,67,69,52,CBS_DROPDOWN | CBS_HASSTRINGS | + WS_VSCROLL | WS_TABSTOP + CONTROL "Short Column Names",IDC_SHORTNAM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,170,65,95,15 + CONTROL "Foreign Keys",IDC_FKSUPPORT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,80,75,15 + CONTROL "Long Column Names",IDC_LONGNAM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,80,95,15 + CONTROL "OEMCP Translation",IDC_OEMCP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,95,75,15 + CONTROL "Don't Create Database",IDC_NOCREAT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,170,95,95,15 + CONTROL "Always BIGINT",IDC_BIGINT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,85,110,75,15 + CONTROL "No WCHAR",IDC_NOWCHAR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,170,110,52,15 + EDITTEXT IDC_LOADEXT,82,130,185,12,ES_AUTOHSCROLL | WS_GROUP + DEFPUSHBUTTON "OK",IDOK,175,150,40,14 + PUSHBUTTON "Cancel",IDCANCEL,225,150,40,14 + CTEXT "Enter options for connect",1003,95,6,80,8 + RTEXT "Data Source Name:",IDC_DSNAMETEXT,6,22,73,9,NOT WS_GROUP + RTEXT "Database Name:",IDC_DBNAMETEXT,7,38,71,9,NOT WS_GROUP + RTEXT "Lock Timeout [ms]:",IDC_TONAMETEXT,6,53,73,9,NOT WS_GROUP + RTEXT "Sync.Mode:",IDC_SYNCPTEXT,6,69,72,9,NOT WS_GROUP + RTEXT "Load Extensions:",IDC_LOADEXTTEXT,6,132,73,9,NOT WS_GROUP +END + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (3) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLite4 ODBC Driver\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "SQLITE4ODBC\0" + VALUE "LegalCopyright", "Copyright © 2013 <chw@ch-werner.de>\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "SQLITE4ODBC.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite4 " SQLITE4_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_MSGTITLE "SQLite4 ODBC Setup" + IDS_BADDSN "%s cannot be used as a data source name." + IDS_EXTTITLE "SQLite4 ODBC Extension" + IDS_EXTERR "Extension '%s' did not load:\n%s" + IDS_DRVTITLE "SQLite4 ODBC Driver" + IDS_DLLERR "No usable SQLite4 DLL found." +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqliteodbc.ico" diff --git a/lang/sql/odbc/sqliteodbc.c b/lang/sql/odbc/sqliteodbc.c index 62f5e7c4..5600f3b0 100644 --- a/lang/sql/odbc/sqliteodbc.c +++ b/lang/sql/odbc/sqliteodbc.c @@ -2,9 +2,9 @@ * @file sqliteodbc.c * SQLite ODBC Driver main module. * - * $Id: sqliteodbc.c,v 1.191 2011/11/15 07:18:18 chw Exp chw $ + * $Id: sqliteodbc.c,v 1.209 2013/12/08 07:18:56 chw Exp chw $ * - * Copyright (c) 2001-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2001-2013 Christian Werner <chw@ch-werner.de> * OS/2 Port Copyright (c) 2004 Lorne R. Sunley <lsunley@mb.sympatico.ca> * * See the file "license.terms" for information on usage @@ -52,7 +52,8 @@ #endif #ifdef _WIN64 -#define CANT_PASS_VALIST_AS_CHARPTR +#undef CANT_PASS_VALIST_AS_CHARPTR +#define CANT_PASS_VALIST_AS_CHARPTR 1 #endif #ifdef CANT_PASS_VALIST_AS_CHARPTR @@ -436,7 +437,7 @@ strdup_(const char *str) /** * Return length of UNICODE string. * @param str UNICODE string - * @result length of string + * @result length of string in characters */ static int @@ -457,7 +458,7 @@ uc_strlen(SQLWCHAR *str) * Copy UNICODE string like strncpy(). * @param dest destination area * @param src source area - * @param len length of source area + * @param len length of source area in characters * @return pointer to destination area */ @@ -482,7 +483,7 @@ uc_strncpy(SQLWCHAR *dest, SQLWCHAR *src, int len) /** * Make UNICODE string from UTF8 string into buffer. * @param str UTF8 string to be converted - * @param len length of str or -1 + * @param len length in characters of str or -1 * @param uc destination area to receive UNICODE string * @param ucLen byte length of destination area */ @@ -536,16 +537,16 @@ uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen) (str[3] & 0xc0) == 0x80) { unsigned long t = ((c & 0x03) << 18) | ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | - (str[4] & 0x3f); + (str[3] & 0x3f); if (sizeof (SQLWCHAR) == 2 * sizeof (char) && t >= 0x10000) { t -= 0x10000; - uc[i++] = 0xd800 | (t & 0x3ff); + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); if (i >= ucLen) { break; } - t = 0xdc00 | ((t >> 10) & 0x3ff); + t = 0xdc00 | (t & 0x3ff); } uc[i++] = t; str += 4; @@ -558,16 +559,16 @@ uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen) (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80) { unsigned long t = ((c & 0x01) << 24) | ((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) | - ((str[4] & 0x3f) << 6) | (str[5] & 0x3f); + ((str[3] & 0x3f) << 6) | (str[4] & 0x3f); if (sizeof (SQLWCHAR) == 2 * sizeof (char) && t >= 0x10000) { t -= 0x10000; - uc[i++] = 0xd800 | (t & 0x3ff); + uc[i++] = 0xd800 | ((t >> 10) & 0x3ff); if (i >= ucLen) { break; } - t = 0xdc00 | ((t >> 10) & 0x3ff); + t = 0xdc00 | (t & 0x3ff); } uc[i++] = t; str += 5; @@ -654,8 +655,8 @@ uc_to_utf(SQLWCHAR *str, int len) c >= 0xd800 && c <= 0xdbff && i + 1 < len) { unsigned long c2 = str[i + 1] & 0xffff; - if (c2 >= 0xdc00 && c <= 0xdfff) { - c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000; + if (c2 >= 0xdc00 && c2 <= 0xdfff) { + c = (((c & 0x3ff) << 10) | (c2 & 0x3ff)) + 0x10000; *cp++ = 0xf0 | ((c >> 18) & 0x07); *cp++ = 0x80 | ((c >> 12) & 0x3f); *cp++ = 0x80 | ((c >> 6) & 0x3f); @@ -973,11 +974,13 @@ extern double sqliteAtoF(char *data, char **endp); static double ln_strtod(const char *data, char **endp) { - struct lconv *lc; + static struct lconv *lc = 0; char buf[128], *p, *end; double value; - lc = localeconv(); + if (!lc) { + lc = localeconv(); + } if (lc && lc->decimal_point && lc->decimal_point[0] && lc->decimal_point[0] != '.') { strncpy(buf, data, sizeof (buf) - 1); @@ -1017,11 +1020,13 @@ static void ln_sprintfg(char *buf, double value) { #if defined(HAVE_LOCALECONV) || defined(_WIN32) || defined(_WIN64) - struct lconv *lc; + static struct lconv *lc = 0; char *p; sprintf(buf, "%.16g", value); - lc = localeconv(); + if (!lc) { + lc = localeconv(); + } if (lc && lc->decimal_point && lc->decimal_point[0] && lc->decimal_point[0] != '.') { p = strchr(buf, lc->decimal_point[0]); @@ -1099,7 +1104,7 @@ unescpat(char *str) * SQL LIKE string match with optional backslash escape handling. * @param str string * @param pat pattern - * @param esc when true, treat literally "\\" as "\", "\?" as "?", "\_" as "_" + * @param esc when true, treat literally "\\" as "\", "\%" as "%", "\_" as "_" * @result true when pattern matched */ @@ -1656,7 +1661,7 @@ mapdeftype(int type, int stype, int nosign, int nowchar) * @param sql original query string * @param sqlLen length of query string or SQL_NTS * @param nparam output number of parameters - * @param isselect output indicator for SELECT statement + * @param isselect output indicator for SELECT (1) or DDL statement (2) * @param errmsg output error message * @param version SQLite version information * @param namepp pointer to parameter names array @@ -1761,14 +1766,32 @@ errout: ++qq; } if (*qq && *qq != ';') { + int i; + static const struct { + int len; + const char *str; + } ddlstr[] = { + { 6, "attach" }, + { 5, "begin" }, + { 6, "commit" }, + { 6, "create" }, + { 6, "detach" }, + { 4, "drop" }, + { 3, "end" }, + { 8, "rollback" }, + { 6, "vacuum" } + }; + size = strlen(qq); - if ((size >= 5) && - (strncasecmp(qq, "create", 5) == 0)) { - isddl = 1; - } else if ((size >= 4) && - (strncasecmp(qq, "drop", 4) == 0)) { - isddl = 1; - } else { + for (i = 0; i < array_size(ddlstr); i++) { + if (size >= ddlstr[i].len && + strncasecmp(qq, ddlstr[i].str, ddlstr[i].len) + == 0) { + isddl = 1; + break; + } + } + if (isddl != 1) { isddl = 0; } } @@ -1793,30 +1816,64 @@ errout: *p++ = '%'; break; case '{': - /* deal with {d 'YYYY-MM-DD'}, {t ...}, and {ts ...} */ + /* + * Deal with escape sequences: + * {d 'YYYY-MM-DD'}, {t ...}, {ts ...} + * {oj ...}, {fn ...} etc. + */ if (!inq) { - char *end = q + 1; + int ojfn = 0; + char *inq2 = NULL, *end = q + 1, *start; - while (*end && *end != '}') { + while (*end && ISSPACE(*end)) { + ++end; + } + if (*end != 'd' && *end != 'D' && + *end != 't' && *end != 'T') { + ojfn = 1; + } + start = end; + while (*end) { + if (inq2 && *end == *inq2) { + inq2 = NULL; + } else if (inq2 == NULL && *end == '}') { + break; + } else if (inq2 == NULL && (*end == '\'' || *end == '"')) { + inq2 = end; + } ++end; } if (*end == '}') { - char *start = q + 1; char *end2 = end - 1; - while (start < end2 && *start != '\'') { - ++start; - } - while (end2 > start && *end2 != '\'') { - --end2; - } - if (*start == '\'' && *end2 == '\'') { - while (start <= end2) { + if (ojfn) { + while (start < end) { + if (ISSPACE(*start)) { + break; + } + ++start; + } + while (start < end) { *p++ = *start; ++start; } q = end; break; + } else { + while (start < end2 && *start != '\'') { + ++start; + } + while (end2 > start && *end2 != '\'') { + --end2; + } + if (*start == '\'' && *end2 == '\'') { + while (start <= end2) { + *p++ = *start; + ++start; + } + q = end; + break; + } } } } @@ -1833,7 +1890,7 @@ errout: } if (isselect) { if (isddl > 0) { - *isselect = 0; + *isselect = 2; } else { int incom = 0; @@ -1866,7 +1923,16 @@ errout: ++p; } size = strlen(p); - *isselect = (size >= 6) && (strncasecmp(p, "select", 6) == 0); + if (size >= 6 && + (strncasecmp(p, "select", 6) == 0 || + strncasecmp(p, "pragma", 6) == 0)) { + *isselect = 1; + } else if (size >= 7 && + strncasecmp(p, "explain", 7) == 0) { + *isselect = 1; + } else { + *isselect = 0; + } } } if (namepp) { @@ -2356,7 +2422,7 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) ++m; } buf[m] = '\0'; - tss->fraction = strtol(buf, NULL, 0); + tss->fraction = strtol(buf, NULL, 10); } m = 7; goto done; @@ -2435,9 +2501,8 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) if (*q == ' ') { if ((m & 1) == 0) { char *e = NULL; - int dummy; - dummy = strtol(q + 1, &e, 10); + (void) strtol(q + 1, &e, 10); if (e && *e == '-') { goto skip; } @@ -2501,7 +2566,7 @@ str2timestamp(char *str, TIMESTAMP_STRUCT *tss) } p = q; q = NULL; - nn = strtol(p, &q, 0); + nn = strtol(p, &q, 10); tss->minute += nn * sign; if ((SQLSMALLINT) tss->minute < 0) { tss->hour -= 1; @@ -2683,11 +2748,21 @@ dbopen(DBC *d, char *name, char *dsn, char *sflag, char *ntflag, char *busy) { char *errp = NULL, *endp = NULL; int tmp, busyto = 100000; +#if defined(_WIN32) || defined(_WIN64) + char expname[MAX_PATH]; +#endif if (d->sqlite) { sqlite_close(d->sqlite); d->sqlite = NULL; } +#if defined(_WIN32) || defined(_WIN64) + expname[0] = '\0'; + tmp = ExpandEnvironmentStrings(name, expname, sizeof (expname)); + if (tmp <= sizeof (expname)) { + name = expname; + } +#endif d->sqlite = sqlite_open(name, 0, &errp); if (d->sqlite == NULL) { connfail: @@ -2720,15 +2795,15 @@ connfail: busyto = 1000000; } d->timeout = busyto; + freep(&d->dbname); + d->dbname = xstrdup(name); + freep(&d->dsn); + d->dsn = xstrdup(dsn); if (setsqliteopts(d->sqlite, d) != SQLITE_OK) { sqlite_close(d->sqlite); d->sqlite = NULL; goto connfail; } - freep(&d->dbname); - d->dbname = xstrdup(name); - freep(&d->dsn); - d->dsn = xstrdup(dsn); #if defined(_WIN32) || defined(_WIN64) { char pname[MAX_PATH]; @@ -3391,7 +3466,7 @@ seqerr: setstat(s, -1, "sequence error", "HY010"); return SQL_ERROR; } - for (i = 0; i < s->nparams; i++) { + for (i = (s->pdcount < 0) ? 0 : s->pdcount; i < s->nparams; i++) { p = &s->bindparms[i]; if (p->need > 0) { int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]); @@ -3418,17 +3493,17 @@ seqerr: #ifdef SQL_BIT case SQL_C_BIT: #endif - size = sizeof (char); + size = sizeof (SQLCHAR); break; case SQL_C_SHORT: case SQL_C_USHORT: case SQL_C_SSHORT: - size = sizeof (short); + size = sizeof (SQLSMALLINT); break; case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_SLONG: - size = sizeof (long); + size = sizeof (SQLINTEGER); break; case SQL_C_FLOAT: size = sizeof (float); @@ -3674,6 +3749,14 @@ substparam(STMT *s, int pnum, char **outp) isnull = 1; goto bind; } +#if (HAVE_ENCDEC) + if (type == SQL_C_CHAR && + (p->stype == SQL_BINARY || + p->stype == SQL_VARBINARY || + p->stype == SQL_LONGVARBINARY)) { + type = SQL_C_BINARY; + } +#endif switch (type) { case SQL_C_CHAR: #ifdef WCHARSUPPORT @@ -3792,10 +3875,10 @@ substparam(STMT *s, int pnum, char **outp) (*s->ov3) ? "07009" : "S1093"); return SQL_ERROR; } - if (!p->parbuf && p->lenp) { + if (!p->parbuf) { #ifdef WCHARSUPPORT if (type == SQL_C_WCHAR) { - if (*p->lenp == SQL_NTS) { + if (!p->lenp || *p->lenp == SQL_NTS) { p->max = uc_strlen(p->param) * sizeof (SQLWCHAR); } else if (*p->lenp >= 0) { p->max = *p->lenp; @@ -3803,7 +3886,7 @@ substparam(STMT *s, int pnum, char **outp) } else #endif if (type == SQL_C_CHAR) { - if (*p->lenp == SQL_NTS) { + if (!p->lenp || *p->lenp == SQL_NTS) { p->len = p->max = strlen(p->param); } else if (*p->lenp >= 0) { p->len = p->max = *p->lenp; @@ -3812,7 +3895,7 @@ substparam(STMT *s, int pnum, char **outp) } #if (HAVE_ENCDEC) else if (type == SQL_C_BINARY) { - p->len = p->max = *p->lenp; + p->len = p->max = p->lenp ? *p->lenp : 0; } #endif } @@ -4138,7 +4221,11 @@ static SQLRETURN setupparbuf(STMT *s, BINDPARM *p) { if (!p->parbuf) { - p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp); + if (*p->lenp == SQL_DATA_AT_EXEC) { + p->len = p->max; + } else { + p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp); + } if (p->len < 0 && p->len != SQL_NTS && p->len != SQL_NULL_DATA) { setstat(s, -1, "invalid length", "HY009"); @@ -4171,6 +4258,7 @@ SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind) int i; SQLPOINTER dummy; SQLRETURN ret; + BINDPARM *p; HSTMT_LOCK(stmt); if (stmt == SQL_NULL_HSTMT) { @@ -4180,12 +4268,21 @@ SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind) if (!pind) { pind = &dummy; } - for (i = 0; i < s->nparams; i++) { - BINDPARM *p = &s->bindparms[i]; - + if (s->pdcount < s->nparams) { + s->pdcount++; + } + for (i = 0; i < s->pdcount; i++) { + p = &s->bindparms[i]; + if (p->need > 0) { + p->need = -1; + } + } + for (; i < s->nparams; i++) { + p = &s->bindparms[i]; if (p->need > 0) { *pind = (SQLPOINTER) p->param0; ret = setupparbuf(s, p); + s->pdcount = i; goto done; } } @@ -6873,7 +6970,7 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, { DBC *d = NULL; STMT *s = NULL; - int len, naterr; + int len, naterr, strbuf = 1; char *logmsg, *sqlst, *clrmsg = NULL; SQLRETURN ret = SQL_ERROR; @@ -6906,8 +7003,18 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, return SQL_INVALID_HANDLE; } if (buflen < 0) { - ret = SQL_ERROR; - goto done; + switch (buflen) { + case SQL_IS_POINTER: + case SQL_IS_UINTEGER: + case SQL_IS_INTEGER: + case SQL_IS_USMALLINT: + case SQL_IS_SMALLINT: + strbuf = 0; + break; + default: + ret = SQL_ERROR; + goto done; + } } if (recno > 1) { ret = SQL_NO_DATA; @@ -6938,7 +7045,9 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, logmsg = sqlst; break; case SQL_DIAG_MESSAGE_TEXT: - clrmsg = logmsg; + if (info) { + clrmsg = logmsg; + } break; case SQL_DIAG_NUMBER: naterr = 1; @@ -6954,6 +7063,27 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, } ret = SQL_SUCCESS; goto done; + case SQL_DIAG_DYNAMIC_FUNCTION: + logmsg = ""; + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = (s->isselect == 1 || s->isselect == -1) ? s->nrows : 0; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; + case SQL_DIAG_ROW_COUNT: + if (htype == SQL_HANDLE_STMT) { + SQLULEN count; + + count = s->isselect ? 0 : s->nrows; + *((SQLULEN *) info) = count; + ret = SQL_SUCCESS; + } + goto done; default: ret = SQL_ERROR; goto done; @@ -6969,16 +7099,18 @@ drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, if (stringlen) { *stringlen = len; } - if (len >= buflen) { - if (info && buflen > 0) { - if (stringlen) { - *stringlen = buflen - 1; + if (strbuf) { + if (len >= buflen) { + if (info && buflen > 0) { + if (stringlen) { + *stringlen = buflen - 1; + } + strncpy((char *) info, logmsg, buflen); + ((char *) info)[buflen - 1] = '\0'; } - strncpy((char *) info, logmsg, buflen); - ((char *) info)[buflen - 1] = '\0'; + } else if (info) { + strcpy((char *) info, logmsg); } - } else if (info) { - strcpy((char *) info, logmsg); } if (clrmsg) { *clrmsg = '\0'; @@ -7049,6 +7181,7 @@ SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, case SQL_DIAG_SERVER_NAME: case SQL_DIAG_SQLSTATE: case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: if (len > 0) { SQLWCHAR *m = NULL; @@ -7083,6 +7216,7 @@ SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno, case SQL_DIAG_SERVER_NAME: case SQL_DIAG_SQLSTATE: case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_DYNAMIC_FUNCTION: len *= sizeof (SQLWCHAR); break; } @@ -7750,7 +7884,7 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, char dummyc[16]; SQLSMALLINT dummy; #if defined(_WIN32) || defined(_WIN64) - char drvname[301]; + char pathbuf[301], *drvname; #else static char drvname[] = #ifdef __OS2__ @@ -7844,7 +7978,16 @@ drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, break; case SQL_DRIVER_NAME: #if defined(_WIN32) || defined(_WIN64) - GetModuleFileName(hModule, drvname, sizeof (drvname)); + GetModuleFileName(hModule, pathbuf, sizeof (pathbuf)); + drvname = strrchr(pathbuf, '\\'); + if (drvname == NULL) { + drvname = strrchr(pathbuf, '/'); + } + if (drvname == NULL) { + drvname = pathbuf; + } else { + drvname++; + } #endif strmak(val, drvname, valMax, valLen); break; @@ -8330,7 +8473,7 @@ SQLGetInfoW(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax, } } } else { - len = 0; + len *= sizeof (SQLWCHAR); } break; } @@ -8354,14 +8497,12 @@ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC dbc, SQLUSMALLINT func, SQLUSMALLINT *flags) { - DBC *d; int i; SQLUSMALLINT exists[100]; if (dbc == SQL_NULL_HDBC) { return SQL_INVALID_HANDLE; } - d = (DBC *) dbc; for (i = 0; i < array_size(exists); i++) { exists[i] = SQL_FALSE; } @@ -9517,7 +9658,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, { DBC *d; int len; - char buf[SQL_MAX_MESSAGE_LENGTH], dbname[SQL_MAX_MESSAGE_LENGTH / 4]; + char buf[SQL_MAX_MESSAGE_LENGTH * 6], dbname[SQL_MAX_MESSAGE_LENGTH]; char dsn[SQL_MAX_MESSAGE_LENGTH / 4], busy[SQL_MAX_MESSAGE_LENGTH / 4]; char sflag[32], ntflag[32], lnflag[32]; #if defined(HAVE_SQLITETRACE) && (HAVE_SQLITETRACE) @@ -10202,14 +10343,8 @@ unbindcols(STMT *s) { int i; - s->bkmrkcol.type = -1; - s->bkmrkcol.max = 0; - s->bkmrkcol.lenp = NULL; - s->bkmrkcol.valp = NULL; - s->bkmrkcol.index = 0; - s->bkmrkcol.offs = 0; for (i = 0; s->bindcols && i < s->nbindcols; i++) { - s->bindcols[i].type = -1; + s->bindcols[i].type = SQL_UNKNOWN_TYPE; s->bindcols[i].max = 0; s->bindcols[i].lenp = NULL; s->bindcols[i].valp = NULL; @@ -10238,7 +10373,7 @@ mkbindcols(STMT *s, int ncols) return nomem(s); } for (i = s->nbindcols; i < ncols; i++) { - bindcols[i].type = -1; + bindcols[i].type = SQL_UNKNOWN_TYPE; bindcols[i].max = 0; bindcols[i].lenp = NULL; bindcols[i].valp = NULL; @@ -10278,27 +10413,34 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, { char **data, valdummy[16]; SQLLEN dummy; + SQLINTEGER *ilenp = NULL; int valnull = 0; int type = otype; + SQLRETURN sret = SQL_NO_DATA; if (!lenp) { lenp = &dummy; } - if (!s->rows) { - *lenp = SQL_NULL_DATA; - return SQL_NO_DATA; + /* workaround for JDK 1.7.0 on x86_64 */ + if (((SQLINTEGER *) lenp) + 1 == (SQLINTEGER *) val) { + ilenp = (SQLINTEGER *) lenp; + lenp = &dummy; } if (col >= s->ncols) { setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002"); return SQL_ERROR; } - if (s->rowp < 0 || s->rowp >= s->nrows) { - *lenp = SQL_NULL_DATA; - return SQL_NO_DATA; - } if (s->retr_data != SQL_RD_ON) { return SQL_SUCCESS; } + if (!s->rows) { + *lenp = SQL_NULL_DATA; + goto done; + } + if (s->rowp < 0 || s->rowp >= s->nrows) { + *lenp = SQL_NULL_DATA; + goto done; + } type = mapdeftype(type, s->cols[col].type, s->cols[col].nosign ? 1 : 0, s->nowchar[0]); @@ -10487,10 +10629,12 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, *lenp = 0; if (!dlen && s->bindcols[col].offs == dlen) { s->bindcols[col].offs = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } s->bindcols[col].offs = 0; - return SQL_NO_DATA; + sret = SQL_NO_DATA; + goto done; } offs = s->bindcols[col].offs; dlen -= offs; @@ -10514,14 +10658,16 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, if (s->bindcols[col].lenp) { *s->bindcols[col].lenp = dlen; } - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } s->bindcols[col].offs += *lenp; } if (*lenp == SQL_NO_TOTAL) { *lenp = dlen; setstat(s, -1, "data right truncated", "01004"); - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } break; } @@ -10546,7 +10692,8 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, ((char *) val)[0] = data[0][0]; memset((char *) val + 1, 0, len - 1); *lenp = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } } #endif @@ -10592,10 +10739,12 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, } if (!dlen && s->bindcols[col].offs == dlen) { s->bindcols[col].offs = 1; - return SQL_SUCCESS; + sret = SQL_SUCCESS; + goto done; } s->bindcols[col].offs = 0; - return SQL_NO_DATA; + sret = SQL_NO_DATA; + goto done; } offs = s->bindcols[col].offs; dlen -= offs; @@ -10644,14 +10793,16 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, if (s->bindcols[col].lenp) { *s->bindcols[col].lenp = dlen; } - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } s->bindcols[col].offs += *lenp; } if (*lenp == SQL_NO_TOTAL) { *lenp = dlen; setstat(s, -1, "data right truncated", "01004"); - return SQL_SUCCESS_WITH_INFO; + sret = SQL_SUCCESS_WITH_INFO; + goto done; } break; } @@ -10702,7 +10853,12 @@ getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype, return SQL_ERROR; } } - return SQL_SUCCESS; + sret = SQL_SUCCESS; +done: + if (ilenp) { + *ilenp = *lenp; + } + return sret; } /** @@ -10729,12 +10885,12 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, s = (STMT *) stmt; if (col < 1) { if (col == 0 && s->bkmrk && type == SQL_C_BOOKMARK) { - s->bkmrkcol.type = type; - s->bkmrkcol.max = sizeof (SQLINTEGER); - s->bkmrkcol.lenp = lenp; + s->bkmrkcol.type = val ? type : SQL_UNKNOWN_TYPE; + s->bkmrkcol.max = val ? sizeof (SQLINTEGER) : 0; + s->bkmrkcol.lenp = val ? lenp : 0; s->bkmrkcol.valp = val; s->bkmrkcol.offs = 0; - if (lenp) { + if (val && lenp) { *lenp = 0; } return SQL_SUCCESS; @@ -10783,7 +10939,7 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, break; case SQL_C_CHAR: break; -#ifdef WINTERFACE +#ifdef WCHARSUPPORT case SQL_C_WCHAR: break; #endif @@ -10821,7 +10977,7 @@ drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type, } if (val == NULL) { /* unbind column */ - s->bindcols[col].type = -1; + s->bindcols[col].type = SQL_UNKNOWN_TYPE; s->bindcols[col].max = 0; s->bindcols[col].lenp = NULL; s->bindcols[col].valp = NULL; @@ -11823,7 +11979,6 @@ drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype) { SQLRETURN ret; STMT *s; - DBC *d; int asize; ret = mkresultset(stmt, typeSpec2, array_size(typeSpec2), @@ -11832,7 +11987,6 @@ drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype) return ret; } s = (STMT *) stmt; - d = (DBC *) s->dbc; #ifdef WINTERFACE #ifdef SQL_LONGVARCHAR #ifdef SQL_WLONGVARCHAR @@ -12612,7 +12766,7 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) i = 0; goto done2; } - if (!s->isselect) { + if (s->isselect != 1 && s->isselect != -1) { setstat(s, -1, "no result set available", "24000"); ret = SQL_ERROR; i = s->nrows; @@ -12659,7 +12813,7 @@ drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset) } break; case SQL_FETCH_PRIOR: - if (s->nrows < 1) { + if (s->nrows < 1 || s->rowp <= 0) { s->rowp = -1; return SQL_NO_DATA; } @@ -12858,7 +13012,7 @@ SQLRowCount(SQLHSTMT stmt, SQLLEN *nrows) } s = (STMT *) stmt; if (nrows) { - *nrows = s->nrows; + *nrows = s->isselect ? 0 : s->nrows; } HSTMT_UNLOCK(stmt); return SQL_SUCCESS; @@ -14276,7 +14430,7 @@ noconn: #endif errp = NULL; freeresult(s, -1); - if (s->isselect > 0) { + if (s->isselect == 1) { int ret; char **params = NULL; @@ -14394,11 +14548,13 @@ unbound: SQLLEN *lenp = p->lenp; if (lenp && *lenp < 0 && *lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && - *lenp != SQL_NTS && *lenp != SQL_NULL_DATA) { + *lenp != SQL_NTS && *lenp != SQL_NULL_DATA && + *lenp != SQL_DATA_AT_EXEC) { setstat(s, -1, "invalid length reference", "HY009"); return SQL_ERROR; } - if (lenp && *lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (lenp && (*lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -14413,6 +14569,7 @@ again: vm_end(s); if (initial) { /* fixup data-at-execution parameters and alloc'ed blobs */ + s->pdcount = -1; for (i = 0; i < s->nparams; i++) { BINDPARM *p = &s->bindparms[i]; @@ -14421,7 +14578,8 @@ again: } freep(&p->parbuf); if (p->need <= 0 && - p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -14453,7 +14611,7 @@ again: } } freeresult(s, 0); - if (s->isselect > 0 && !d->intrans && + if (s->isselect == 1 && !d->intrans && s->curtype == SQL_CURSOR_FORWARD_ONLY && d->step_enable && s->nparams == 0 && d->vm_stmt == NULL) { s->nrows = -1; @@ -14509,10 +14667,9 @@ again: errp = NULL; } s->rowfree = sqlite_free_table; - if (ncols == 1 && !s->isselect) { + if (ncols == 1 && (s->isselect <= 0 || s->isselect > 1)) { /* - * INSERT/UPDATE/DELETE results are immediately released, - * but the row count is retained for SQLRowCount(). + * INSERT/UPDATE/DELETE or DDL results are immediately released. */ if (strcmp(s->rows[0], "rows inserted") == 0 || strcmp(s->rows[0], "rows updated") == 0 || @@ -14606,7 +14763,8 @@ done2: } else if (p->lenp0 && p->inc > 0) { p->lenp = p->lenp0 + s->paramset_count; } - if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { if (p->param0 && s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) { p->param = (char *) p->param0 + @@ -14615,7 +14773,8 @@ done2: p->param = (char *) p->param0 + s->paramset_count * p->inc; } - } else if (p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) { + } else if (p->lenp && (*p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET || + *p->lenp == SQL_DATA_AT_EXEC)) { p->need = 1; p->offs = 0; p->len = 0; @@ -14632,7 +14791,8 @@ cleanup: p->param = NULL; } freep(&p->parbuf); - if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) { + if (!p->lenp || (*p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET && + *p->lenp != SQL_DATA_AT_EXEC)) { p->param = p->param0; } p->lenp = p->lenp0; @@ -14796,12 +14956,12 @@ done: #endif -#define MAXPATHLEN (255+1) /* Max path length */ +#define MAXPATHLEN (259+1) /* Max path length */ #define MAXKEYLEN (15+1) /* Max keyword length */ #define MAXDESC (255+1) /* Max description length */ #define MAXDSNAME (32+1) /* Max data source name length */ #define MAXTONAME (32+1) /* Max timeout length */ -#define MAXDBNAME (255+1) +#define MAXDBNAME MAXPATHLEN /* Attribute key indexes into an array of Attr structs, see below */ @@ -15094,7 +15254,7 @@ ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) switch (wmsg) { case WM_INITDIALOG: #ifdef _WIN64 - SetWindowLong(hdlg, DWLP_USER, lparam); + SetWindowLongPtr(hdlg, DWLP_USER, lparam); #else SetWindowLong(hdlg, DWL_USER, lparam); #endif @@ -15378,7 +15538,7 @@ DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam) switch (wmsg) { case WM_INITDIALOG: #ifdef _WIN64 - SetWindowLong(hdlg, DWLP_USER, lparam); + SetWindowLongPtr(hdlg, DWLP_USER, lparam); #else SetWindowLong(hdlg, DWL_USER, lparam); #endif @@ -15556,7 +15716,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, SQLCHAR *connOut, SQLSMALLINT connOutMax, SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl) { - BOOL maybeprompt, prompt = FALSE; + BOOL maybeprompt, prompt = FALSE, defaultdsn = FALSE; DBC *d; SETUPDLG *setupdlg; SQLRETURN ret; @@ -15584,6 +15744,7 @@ drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd, if (!setupdlg->attr[KEY_DSN].attr[0] && drvcompl == SQL_DRIVER_COMPLETE_REQUIRED) { strcpy(setupdlg->attr[KEY_DSN].attr, "DEFAULT"); + defaultdsn = TRUE; } GetAttributes(setupdlg); if (drvcompl == SQL_DRIVER_PROMPT || @@ -15616,9 +15777,10 @@ retry: } } if (connOut || connOutLen) { - char buf[1024]; + char buf[SQL_MAX_MESSAGE_LENGTH * 4]; int len, count; - char dsn_0 = setupdlg->attr[KEY_DSN].attr[0]; + char dsn_0 = (setupdlg->attr[KEY_DSN].attr[0] && !defaultdsn) ? + setupdlg->attr[KEY_DSN].attr[0] : '\0'; char drv_0 = setupdlg->attr[KEY_DRIVER].attr[0]; buf[0] = '\0'; @@ -15740,6 +15902,9 @@ SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd, HDBC_LOCK(dbc); if (connIn) { + if (connInLen == SQL_NTS) { + connInLen = -1; + } ci = uc_to_utf_c(connIn, connInLen); if (!ci) { DBC *d = (DBC *) dbc; @@ -15760,9 +15925,8 @@ SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd, if (len > 0) { co = uc_from_utf((SQLCHAR *) connOut, len); if (co) { - uc_strncpy(connOut, co, connOutMax); - co[len] = 0; - len = min(connOutMax, uc_strlen(co)); + uc_strncpy(connOut, co, connOutMax / sizeof (SQLWCHAR)); + len = min(connOutMax / sizeof (SQLWCHAR), uc_strlen(co)); uc_free(co); } else { len = 0; @@ -15974,7 +16138,7 @@ InUn(int remove, char *cmdline) if (GetFileAttributesA(dllbuf) == INVALID_FILE_ATTRIBUTES) { return FALSE; } - if (strcmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) { + if (strcasecmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) { char buf[512]; sprintf(buf, "Copy %s to %s failed.", dllbuf, inst); diff --git a/lang/sql/odbc/sqliteodbc.h b/lang/sql/odbc/sqliteodbc.h index 7ecde11b..65f47be5 100644 --- a/lang/sql/odbc/sqliteodbc.h +++ b/lang/sql/odbc/sqliteodbc.h @@ -15,9 +15,9 @@ * @file sqliteodbc.h * Header file for SQLite ODBC driver. * - * $Id: sqliteodbc.h,v 1.56 2011/11/12 04:35:40 chw Exp chw $ + * $Id: sqliteodbc.h,v 1.59 2013/01/11 12:20:56 chw Exp chw $ * - * Copyright (c) 2001-2011 Christian Werner <chw@ch-werner.de> + * Copyright (c) 2001-2013 Christian Werner <chw@ch-werner.de> * * See the file "license.terms" for information on usage * and redistribution of this file and for a @@ -246,6 +246,7 @@ typedef struct stmt { int nbindparms; /**< Number bound parameters */ BINDPARM *bindparms; /**< Array of bound parameters */ int nparams; /**< Number of parameters in query */ + int pdcount; /**< SQLParamData() counter */ int nrows; /**< Number of result rows */ int rowp; /**< Current result row */ char **rows; /**< 2-dim array, result set */ diff --git a/lang/sql/odbc/sqliteodbc.mak b/lang/sql/odbc/sqliteodbc.mak new file mode 100644 index 00000000..4fa02bdf --- /dev/null +++ b/lang/sql/odbc/sqliteodbc.mak @@ -0,0 +1,108 @@ +# VC++ 6 Makefile + +CC= cl +LN= link +RC= rc + +!IF "$(DEBUG)" == "1" +LDEBUG= /DEBUG +CDEBUG= -Zi +!ELSE +LDEBUG= /RELEASE +!ENDIF + +!IF "$(ENCODING)" == "" +ENCODING= ISO8859 +!ENDIF + +CFLAGS= -I. -Isqlite -Gs -GX -D_WIN32 -D_DLL -nologo $(CDEBUG) \ + -DHAVE_ENCDEC=1 -DHAVE_LIBVERSION=1 -DHAVE_SQLITEATOF=1 \ + -DHAVE_SQLITEMPRINTF=1 +CFLAGSEXE= -I. -Gs -GX -D_WIN32 -nologo $(CDEBUG) +DLLLFLAGS= /NODEFAULTLIB $(LDEBUG) /NOLOGO /MACHINE:IX86 \ + /SUBSYSTEM:WINDOWS /DLL +DLLLIBS= msvcrt.lib odbccp32.lib kernel32.lib \ + user32.lib comdlg32.lib sqlite\libsqlite.lib + +!IF "$(ENCODING)" == "UTF8" +DRVDLL= sqliteodbcu.dll +!ELSE +DRVDLL= sqliteodbc.dll +!ENDIF + +OBJECTS= sqliteodbc.obj + +.c.obj: + $(CC) $(CFLAGS) /c $< + +all: $(DRVDLL) inst.exe uninst.exe adddsn.exe remdsn.exe \ + addsysdsn.exe remsysdsn.exe SQLiteODBCInstaller.exe + +clean: + del *.obj + del *.res + del *.exp + del *.ilk + del *.pdb + del *.res + del resource.h + del *.exe + cd sqlite + nmake -f ..\sqlite.mak clean + cd .. + +uninst.exe: inst.exe + copy inst.exe uninst.exe + +inst.exe: inst.c + $(CC) $(CFLAGSEXE) inst.c odbc32.lib odbccp32.lib \ + kernel32.lib user32.lib + +remdsn.exe: adddsn.exe + copy adddsn.exe remdsn.exe + +adddsn.exe: adddsn.c + $(CC) $(CFLAGSEXE) adddsn.c odbc32.lib odbccp32.lib \ + kernel32.lib user32.lib + +remsysdsn.exe: adddsn.exe + copy adddsn.exe remsysdsn.exe + +addsysdsn.exe: adddsn.exe + copy adddsn.exe addsysdsn.exe + +fixup.exe: fixup.c + $(CC) $(CFLAGSEXE) fixup.c + +mkopc.exe: mkopc.c + $(CC) $(CFLAGSEXE) mkopc.c + +SQLiteODBCInstaller.exe: SQLiteODBCInstaller.c + $(CC) $(CFLAGSEXE) SQLiteODBCInstaller.c \ + kernel32.lib user32.lib + +sqliteodbc.c: resource.h + +sqliteodbc.res: sqliteodbc.rc resource.h + $(RC) -I. -Isqlite -fo sqliteodbc.res -r sqliteodbc.rc + +sqliteodbc.dll: sqlite\libsqlite.lib $(OBJECTS) sqliteodbc.res + $(LN) $(DLLLFLAGS) $(OBJECTS) sqliteodbc.res \ + -def:sqliteodbc.def -out:$@ $(DLLLIBS) + +sqliteodbcu.dll: sqlite\libsqlite.lib $(OBJECTS) sqliteodbc.res + $(LN) $(DLLLFLAGS) $(OBJECTS) sqliteodbc.res \ + -def:sqliteodbcu.def -out:$@ $(DLLLIBS) + +VERSION_C: VERSION + .\fixup < VERSION > VERSION_C . , + +resource.h: resource.h.in VERSION_C fixup.exe + .\fixup < resource.h.in > resource.h \ + --VERS-- @VERSION \ + --VERS_C-- @VERSION_C + +sqlite\libsqlite.lib: fixup.exe mkopc.exe + cd sqlite + nmake -f ..\sqlite.mak ENCODING=$(ENCODING) + cd .. diff --git a/lang/sql/odbc/sqliteodbc.nsi b/lang/sql/odbc/sqliteodbc.nsi index 00cb92ab..d8568af5 100644 --- a/lang/sql/odbc/sqliteodbc.nsi +++ b/lang/sql/odbc/sqliteodbc.nsi @@ -101,7 +101,7 @@ Section "-Main (required)" InstallationInfo File "remdsn.exe" File "addsysdsn.exe" File "remsysdsn.exe" - File "SQLiteODBCInstaller.exe" +; File "SQLiteODBCInstaller.exe" ; SQLite 3.4.* ; File "sqlite3_mod_fts1.dll" ; File "sqlite3_mod_fts2.dll" @@ -109,9 +109,11 @@ Section "-Main (required)" InstallationInfo File "sqlite3_mod_fts3.dll" File "sqlite3_mod_blobtoxy.dll" File "sqlite3_mod_impexp.dll" + File "sqlite3_mod_csvtable.dll" ; SQLite 3.6.* File "sqlite3_mod_rtree.dll" File "sqlite3_mod_extfunc.dll" + File "sqlite3_mod_zipfile.dll" File "license.terms" File "license.txt" File "README" @@ -228,7 +230,7 @@ Section /o "Source Code" SourceInstall File "source\mf-sqlite3rtree.mingw-cross" File "source\mingw-cross-build.sh" File "source\sqliteodbc.nsi" - File "source\SQLiteODBCInstaller.c" +; File "source\SQLiteODBCInstaller.c" File "source\blobtoxy.c" File "source\blobtoxy.rc" File "source\impexp.c" diff --git a/lang/sql/odbc/sqliteodbc.rc b/lang/sql/odbc/sqliteodbc.rc index ad21151e..790a9635 100644 --- a/lang/sql/odbc/sqliteodbc.rc +++ b/lang/sql/odbc/sqliteodbc.rc @@ -102,7 +102,7 @@ BEGIN #endif VALUE "FileVersion", VERSION "\0" VALUE "InternalName", "SQLITEODBC\0" - VALUE "LegalCopyright", "Copyright © 2001-2011 <chw@ch-werner.de>\0" + VALUE "LegalCopyright", "Copyright © 2001-2013 <chw@ch-werner.de>\0" VALUE "LegalTrademarks", "\0" #ifdef SQLITE_UTF8 VALUE "OriginalFilename", "SQLITEODBCU.DLL\0" diff --git a/lang/sql/odbc/sqliteodbc.spec b/lang/sql/odbc/sqliteodbc.spec index 03c0d9cc..041a0463 100644 --- a/lang/sql/odbc/sqliteodbc.spec +++ b/lang/sql/odbc/sqliteodbc.spec @@ -1,5 +1,5 @@ %define name sqliteodbc -%define version 0.93 +%define version 0.996 %define release 1 Name: %{name} @@ -102,5 +102,5 @@ fi %{_libdir}/*.so* %changelog -* Tue Nov 15 2011 ... +* Sun Dec 08 2013 ... - automatically recreated by configure ... diff --git a/lang/sql/odbc/sqliteodbc_w64.nsi b/lang/sql/odbc/sqliteodbc_w64.nsi index eacafe63..bee6bfe1 100644 --- a/lang/sql/odbc/sqliteodbc_w64.nsi +++ b/lang/sql/odbc/sqliteodbc_w64.nsi @@ -22,11 +22,11 @@ Name "SQLite3 ODBC Driver for Win64" !endif !ifdef WITH_SEE -!define PROD_NAME "SQLite3 ODBC Driver (SEE) for Win64" -!define PROD_NAME0 "SQLite3 ODBC Driver (SEE) for Win64" +!define PROD_NAME "SQLite ODBC Driver (SEE) for Win64" +!define PROD_NAME0 "SQLite ODBC Driver (SEE) for Win64" !else -!define PROD_NAME "SQLite3 ODBC Driver for Win64" -!define PROD_NAME0 "SQLite3 ODBC Driver for Win64" +!define PROD_NAME "SQLite ODBC Driver for Win64" +!define PROD_NAME0 "SQLite ODBC Driver for Win64" !endif CRCCheck On !include "MUI.nsh" @@ -47,15 +47,18 @@ InstallDir "$PROGRAMFILES64\${PROD_NAME0}" !define MUI_ICON "sqliteodbc.ico" !define MUI_UNICON "sqliteodbc.ico" -!define MUI_WELCOMEPAGE_TITLE "SQLite3 ODBC for Win64 Installation" +!define MUI_WELCOMEPAGE_TITLE "SQLite ODBC for Win64 Installation" !define MUI_WELCOMEPAGE_TEXT "This program will guide you through the \ installation of SQLite ODBC Driver.\r\n\r\n$_CLICK" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "license.txt" !insertmacro MUI_PAGE_DIRECTORY +!ifndef WITHOUT_SQLITE2 +!insertmacro MUI_PAGE_COMPONENTS +!endif !insertmacro MUI_PAGE_INSTFILES -!define MUI_FINISHPAGE_TITLE "SQLite3 ODBC for Win64 Installation" +!define MUI_FINISHPAGE_TITLE "SQLite ODBC for Win64 Installation" !define MUI_FINISHPAGE_TEXT "The installation of SQLite ODBC Driver is complete.\ \r\n\r\n$_CLICK" @@ -101,8 +104,10 @@ Section "-Main (required)" InstallationInfo File "sqlite3_mod_fts3.dll" File "sqlite3_mod_blobtoxy.dll" File "sqlite3_mod_impexp.dll" + File "sqlite3_mod_csvtable.dll" File "sqlite3_mod_rtree.dll" File "sqlite3_mod_extfunc.dll" + File "sqlite3_mod_zipfile.dll" File "license.terms" File "license.txt" File "README" @@ -142,6 +147,27 @@ Section "-Main (required)" InstallationInfo SectionEnd +!ifndef WITHOUT_SQLITE2 +Section /o "SQLite 2 Drivers" Sqlite2Install + SetOutPath "$INSTDIR" + File "sqliteodbc.dll" + File "sqliteodbcu.dll" + File "sqlite.exe" + File "sqliteu.exe" +!ifdef WITH_SQLITE_DLLS + File "sqlite.dll" + File "sqliteu.dll" +!endif + + CreateShortCut "$SMPROGRAMS\${PROD_NAME0}\Shells\SQLite 2.lnk" \ + "$INSTDIR\sqlite.exe" + CreateShortCut "$SMPROGRAMS\${PROD_NAME0}\Shells\SQLite 2 (UTF-8).lnk" \ + "$INSTDIR\sqliteu.exe" + + ExecWait '"$INSTDIR\instq.exe"' +SectionEnd +!endif + ;-------------------------------- ; Uninstaller Section diff --git a/lang/sql/odbc/xpath.c b/lang/sql/odbc/xpath.c new file mode 100644 index 00000000..a8349bcf --- /dev/null +++ b/lang/sql/odbc/xpath.c @@ -0,0 +1,1664 @@ +/** + * @file xpath.c + * SQLite extension module to select parts of XML documents + * using libxml2 XPath and SQLite's virtual table mechanism. + * + * 2013 March 15 + * + * The author disclaims copyright to this source code. + * In place of a legal notice, here is a blessing: + * + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + * + ******************************************************************** + */ + +#if defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#endif +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#ifdef WITH_XSLT +#include <libxslt/xslt.h> +#include <libxslt/transform.h> +#include <libxslt/xsltutils.h> +#endif + +#ifdef STANDALONE +#include <sqlite3.h> +#else +#include <sqlite3ext.h> +static SQLITE_EXTENSION_INIT1 +#endif + +/** + * @typedef XDOC + * @struct XDOC + * Structure to cache XML document. + */ + +typedef struct XDOC { + xmlDocPtr doc; /**< XML document. */ + int refcnt; /**< Reference counter. */ +} XDOC; + +/** + * @typedef XMOD + * @struct XMOD + * Structure holding per module/database data. + */ + +typedef struct XMOD { + int refcnt; /**< Reference counter. */ + sqlite3_mutex *mutex; /**< DOC table mutex. */ + int sdoc; /**< Size of docs array. */ + int ndoc; /**< Number of used entries in docs array. */ + XDOC *docs; /**< Array of modules's DOCs. */ +} XMOD; + +static int initialized = 0; +static XMOD *xmod = 0; + +/** + * @typedef XTAB + * @struct XTAB + * Structure to describe virtual table. + */ + +typedef struct XTAB { + sqlite3_vtab vtab; /**< SQLite virtual table. */ + sqlite3 *db; /**< Open database. */ + XMOD *xm; /**< Module data. */ + struct XCSR *xc; /**< Current cursor. */ + int sdoc; /**< Size of idocs array. */ + int ndoc; /**< Number of used entries in idocs array. */ + int *idocs; /**< Indexes in module-wide DOC table. */ +} XTAB; + +/** + * @typedef XEXP + * @struct XEXP + * Structure to describe XPath expression. + */ + +typedef struct XEXP { + struct XEXP *next; /**< Next item. */ + struct XEXP *prev; /**< Previous item. */ + xmlDocPtr doc; /**< Current XML document. */ + xmlXPathContextPtr pctx; /**< Current XPath context. */ + xmlXPathObjectPtr pobj; /**< Current XPath objects. */ + xmlNodePtr parent; /**< Current parent node or NULL. */ + int pos; /**< Position within XPath expr. */ + int conv; /**< Conversion: string/boolean/number. */ + char expr[1]; /**< XPath expression text. */ +} XEXP; + +/** + * @typedef XCSR + * @struct XCSR + * Structure to describe virtual table cursor. + */ + +typedef struct XCSR { + sqlite3_vtab_cursor cursor; /**< SQLite virtual table cursor */ + int pos; /**< Current index. */ + int nexpr; /**< Number of XPath expr. */ + XEXP *first; /**< First XPath expr. */ + XEXP *last; /**< Last XPath expr. */ +} XCSR; + +/** + * Connect to virtual table. + * @param db SQLite database pointer + * @param aux user specific pointer + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + * + * Argument vector contains: + * + * argv[0] - module name<br> + * argv[1] - database name<br> + * argv[2] - table name (virtual table)<br> + */ + +static int +xpath_connect(sqlite3* db, void *aux, int argc, const char * const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + int rc = SQLITE_ERROR; + XTAB *xt; + + xt = sqlite3_malloc(sizeof (XTAB)); + if (!xt) { +nomem: + *errp = sqlite3_mprintf("out of memory"); + return rc; + } + memset(xt, 0, sizeof (XTAB)); + xt->db = db; + xt->xm = (XMOD *) aux; + xt->xc = 0; + xt->sdoc = 128; + xt->ndoc = 0; + xt->idocs = sqlite3_malloc(xt->sdoc * sizeof (int)); + if (!xt->idocs) { + sqlite3_free(xt); + goto nomem; + } + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(" + " DOCID INTEGER PRIMARY KEY," + " XML HIDDEN BLOB," + " PATH HIDDEN TEXT," + " OPTIONS HIDDEN INTEGER," + " ENCODING HIDDEN TEXT," + " BASEURL HIDDEN TEXT," + " XMLDUMP HIDDEN TEXT" + ")"); + if (rc != SQLITE_OK) { + sqlite3_free(xt->idocs); + sqlite3_free(xt); + *errp = sqlite3_mprintf("table definition failed (error %d)", rc); + return rc; + } + *vtabp = &xt->vtab; + *errp = 0; + return SQLITE_OK; +} + +/** + * Create virtual table. + * @param db SQLite database pointer + * @param aux user specific pointer + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + */ + +static int +xpath_create(sqlite3* db, void *aux, int argc, + const char *const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + return xpath_connect(db, aux, argc, argv, vtabp, errp); +} + +/** + * Disconnect virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +xpath_disconnect(sqlite3_vtab *vtab) +{ + XTAB *xt = (XTAB *) vtab; + XMOD *xm = xt->xm; + int i, n; + + if (xm->mutex) { + sqlite3_mutex_enter(xm->mutex); + for (i = 0; xm->docs && (i < xt->ndoc); i++) { + n = xt->idocs[i]; + if ((n >= 0) && (n < xm->sdoc)) { + xmlDocPtr doc = xm->docs[n].doc; + if (doc) { + xm->docs[n].refcnt -= 1; + if (xm->docs[n].refcnt <= 0) { + xm->docs[n].doc = 0; + xm->docs[n].refcnt = 0; + xm->ndoc--; + xmlFreeDoc(doc); + } + } + } + } + sqlite3_mutex_leave(xm->mutex); + } + sqlite3_free(xt->idocs); + sqlite3_free(xt); + return SQLITE_OK; +} + +/** + * Destroy virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +xpath_destroy(sqlite3_vtab *vtab) +{ + return xpath_disconnect(vtab); +} + +/** + * Determines information for filter function according to constraints. + * @param vtab virtual table + * @param info index/constraint information + * @result SQLite error code + */ + +static int +xpath_bestindex(sqlite3_vtab *vtab, sqlite3_index_info *info) +{ + return SQLITE_OK; +} + +/** + * Open virtual table and return cursor. + * @param vtab virtual table pointer + * @param cursorp pointer receiving cursor pointer + * @result SQLite error code + */ + +static int +xpath_open(sqlite3_vtab *vtab, sqlite3_vtab_cursor **cursorp) +{ + XCSR *xc = sqlite3_malloc(sizeof (XCSR)); + + if (!xc) { + return SQLITE_ERROR; + } + xc->cursor.pVtab = vtab; + xc->pos = -1; + xc->nexpr = 0; + xc->first = xc->last = 0; + *cursorp = &xc->cursor; + return SQLITE_OK; +} + +/** + * Close virtual table cursor. + * @param cursor cursor pointer + * @result SQLite error code + */ + +static int +xpath_close(sqlite3_vtab_cursor *cursor) +{ + XCSR *xc = (XCSR *) cursor; + XEXP *xp = xc->first, *next; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + + while (xp) { + next = xp->next; + if (xp->pobj) { + xmlXPathFreeObject(xp->pobj); + } + if (xp->pctx) { + xmlXPathFreeContext(xp->pctx); + } + sqlite3_free(xp); + xp = next; + } + if (xt->xc == xc) { + xt->xc = 0; + } + sqlite3_free(xc); + return SQLITE_OK; +} + +/** + * Retrieve next row from virtual table cursor. + * @param cursor virtual table cursor + * @result SQLite error code + */ + +static int +xpath_next(sqlite3_vtab_cursor *cursor) +{ + XCSR *xc = (XCSR *) cursor; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + XEXP *xp; + + if (xc->pos < xt->ndoc) { + int ninc = 0; + + if ((xc->pos >= 0) && xc->nexpr) { + int newpos; + xmlNodePtr node, parent = 0; + + xp = xc->first; + while (xp) { + if (xp->pobj) { + if (xp == xc->first) { + parent = xp->parent; + } else if (parent != xp->parent) { + break; + } + } + xp = xp->next; + } + if (parent && !xp) { + int pchg = 0; + + xp = xc->first; + while (xp) { + if (xp->pobj && (xp->pobj->type == XPATH_NODESET) && + xp->pobj->nodesetval) { + newpos = xp->pos + 1; + if (newpos < xp->pobj->nodesetval->nodeNr) { + node = xp->pobj->nodesetval->nodeTab[newpos]; + if (node->parent != xp->parent) { + pchg++; + } + } else { + pchg++; + } + } + xp = xp->next; + } + if ((pchg != 0) && (pchg != xc->nexpr)) { + xp = xc->first; + while (xp) { + if (xp->pobj && (xp->pobj->type == XPATH_NODESET) && + xp->pobj->nodesetval) { + newpos = xp->pos + 1; + if (newpos < xp->pobj->nodesetval->nodeNr) { + node = xp->pobj->nodesetval->nodeTab[newpos]; + if (node->parent == xp->parent) { + xp->pos = newpos; + ninc++; + } + } else { + xp->pos = xp->pobj->nodesetval->nodeNr; + ninc++; + } + } + xp = xp->next; + } + } + } + if (!ninc) { + xp = xc->first; + while (xp) { + if (xp->pobj && (xp->pobj->type == XPATH_NODESET) && + xp->pobj->nodesetval) { + newpos = xp->pos + 1; + if (newpos < xp->pobj->nodesetval->nodeNr) { + xp->pos = newpos; + ninc++; + } else { + xp->pos = xp->pobj->nodesetval->nodeNr; + } + } + xp = xp->next; + } + } + } + if (!ninc) { + xc->pos++; + xp = xc->first; + while (xp) { + xp->pos = -1; + xp->parent = 0; + xp = xp->next; + } + } + } + return SQLITE_OK; +} + +/** + * Filter function for virtual table. + * @param cursor virtual table cursor + * @param idxNum not used + * @param idxStr nod used + * @param argc number arguments (not used) + * @param argv argument (nothing or RHS of filter expression, not used) + * @result SQLite error code + */ + +static int +xpath_filter(sqlite3_vtab_cursor *cursor, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) +{ + XCSR *xc = (XCSR *) cursor; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + + xc->pos = -1; + xt->xc = xc; + return xpath_next(cursor); +} + +/** + * Return end of table state of virtual table cursor. + * @param cursor virtual table cursor + * @result true/false + */ + +static int +xpath_eof(sqlite3_vtab_cursor *cursor) +{ + XCSR *xc = (XCSR *) cursor; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + + return xc->pos >= xt->ndoc; +} + +/** + * Return column data of virtual table. + * @param cursor virtual table cursor + * @param ctx SQLite function context + * @param n column index + * @result SQLite error code + */ + +static int +xpath_column(sqlite3_vtab_cursor *cursor, sqlite3_context *ctx, int n) +{ + XCSR *xc = (XCSR *) cursor; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + XMOD *xm = (XMOD *) xt->xm; + + if ((xc->pos < 0) || (xc->pos >= xt->ndoc)) { + sqlite3_result_error(ctx, "column out of bounds", -1); + return SQLITE_ERROR; + } + if (n == 0) { + n = xt->idocs[xc->pos]; + if (xm->docs[n].doc) { + sqlite3_result_int(ctx, n + 1); + return SQLITE_OK; + } + } else if (n == 6) { + n = xt->idocs[xc->pos]; + if (xm->docs[n].doc) { + xmlChar *dump = 0; + int dump_len = 0; + + xmlDocDumpFormatMemoryEnc(xm->docs[n].doc, &dump, + &dump_len, "utf-8", 1); + if (dump) { + sqlite3_result_text(ctx, (char *) dump, dump_len, + SQLITE_TRANSIENT); + xmlFree(dump); + return SQLITE_OK; + } + } + } + sqlite3_result_null(ctx); + return SQLITE_OK; +} + +/** + * Return current rowid of virtual table cursor. + * @param cursor virtual table cursor + * @param rowidp value buffer to receive current rowid + * @result SQLite error code + */ + +static int +xpath_rowid(sqlite3_vtab_cursor *cursor, sqlite3_int64 *rowidp) +{ + XCSR *xc = (XCSR *) cursor; + XTAB *xt = (XTAB *) xc->cursor.pVtab; + XMOD *xm = (XMOD *) xt->xm; + int n = xt->idocs[xc->pos]; + + if (xm->docs[n].doc) { + *rowidp = (sqlite3_int64) (n + 1); + return SQLITE_OK; + } + return SQLITE_ERROR; +} + +/** + * Insert/delete row into/from virtual table. + * @param vtab virtual table pointer + * @param argc number of arguments + * @param argv argument vector + * @param rowidp value buffer to receive rowid + * @result SQLite error code + * + * Examples: + * + * CREATE VIRTUAL TABLE X USING xpath();<br> + * INSERT INTO X(XML) VALUES('xml-string ...');<br> + * INSERT INTO X(PATH,OPTIONS) VALUES(<url>,0);<br> + * DELETE FROM X WHERE DOCID=<docid>;<br> + * + * Virtual table columns: + * + * DOCID - document identifier and ROWID<br> + * XML - XML string<br> + * PATH - pathname or URL<br> + * OPTIONS - parser options, see libxml's XML_PARSE_* defines<br> + * ENCODING - optional document encoding, default UTF-8<br> + * BASEURL - optional base URL when XML string given<br> + * XMLDUMP - output column, XML dump of document tree<br> + * + * All columns except DOCID are hidden. UPDATE on the virtual table + * is not supported. Default parser options are XML_PARSE_NOERROR, + * XML_PARSE_NOWARNING, and XML_PARSE_NONET. + */ + +static int +xpath_update(sqlite3_vtab *vtab, int argc, sqlite3_value **argv, + sqlite3_int64 *rowidp) +{ + int n = -1, rc = SQLITE_ERROR; + XTAB *xt = (XTAB *) vtab; + XMOD *xm = (XMOD *) xt->xm; + xmlDocPtr doc = 0, docToFree = 0; + + if (argc == 1) { + /* DELETE */ + int i, k = -1; + + n = sqlite3_value_int(argv[0]); + for (i = 0; i < xt->ndoc; i++) { + if ((n - 1) == xt->idocs[i]) { + k = xt->idocs[i]; + memmove(xt->idocs + i, xt->idocs + i + 1, + (xt->ndoc - (i + 1)) * sizeof (int)); + xt->ndoc--; + break; + } + } + if ((k >= 0) && xm->mutex) { + n = k; + doc = xm->docs[n].doc; + } + rc = SQLITE_OK; + } else if ((argc > 1) && (sqlite3_value_type(argv[0]) == SQLITE_NULL)) { + /* INSERT */ + int i, docid; + int opts = (XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET); + char *enc = 0; + + if (sqlite3_value_type(argv[1]) != SQLITE_NULL) { + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("ROWID must be NULL"); + rc = SQLITE_CONSTRAINT; + goto done; + } + if (sqlite3_value_type(argv[2]) != SQLITE_NULL) { + docid = sqlite3_value_int(argv[2]); + if ((sqlite3_value_type(argv[3]) != SQLITE_NULL) || + (sqlite3_value_type(argv[4]) != SQLITE_NULL)) { + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("XML and PATH must be NULL"); + rc = SQLITE_CONSTRAINT; + goto done; + } + sqlite3_mutex_enter(xm->mutex); + for (i = 0; xm->docs && (i < xt->ndoc); i++) { + if ((docid - 1) == xt->idocs[i]) { + sqlite3_mutex_leave(xm->mutex); + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("constraint violation"); + rc = SQLITE_CONSTRAINT; + goto done; + } + } + if ((docid > 0) && (docid <= xm->sdoc)) { + doc = xm->docs[docid - 1].doc; + if (doc) { + xm->docs[docid - 1].refcnt++; + } + } + sqlite3_mutex_leave(xm->mutex); + if (!doc) { + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("invalid DOCID"); + goto done; + } + goto havedoc; + } + if (((sqlite3_value_type(argv[3]) == SQLITE_NULL) && + (sqlite3_value_type(argv[4]) == SQLITE_NULL)) || + ((sqlite3_value_type(argv[3]) != SQLITE_NULL) && + (sqlite3_value_type(argv[4]) != SQLITE_NULL))) { + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("specify one of XML or PATH"); + rc = SQLITE_CONSTRAINT; + goto done; + } + if (sqlite3_value_type(argv[5]) != SQLITE_NULL) { + opts = sqlite3_value_int(argv[5]); + } + if (sqlite3_value_type(argv[6]) != SQLITE_NULL) { + enc = (char *) sqlite3_value_text(argv[6]); + } + if (sqlite3_value_type(argv[4]) != SQLITE_NULL) { + doc = xmlReadFile((char *) sqlite3_value_text(argv[4]), enc, opts); + } else { + char *url = 0; + + if (sqlite3_value_type(argv[7]) != SQLITE_NULL) { + url = (char *) sqlite3_value_text(argv[7]); + } + doc = xmlReadMemory(sqlite3_value_blob(argv[3]), + sqlite3_value_bytes(argv[3]), + url ? url : "", enc, opts); + } + if (!doc) { + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("read error"); + goto done; + } + docToFree = doc; +havedoc: + if (xt->ndoc >= xt->sdoc) { + int *idocs = sqlite3_realloc(xt->idocs, xt->sdoc + + 128 * sizeof (int)); + + if (!idocs) { + goto nomem; + } + xt->idocs = idocs; + xt->sdoc += 128; + } + if (!xm->mutex) { + goto nomem; + } + sqlite3_mutex_enter(xm->mutex); + if (xm->ndoc >= xt->sdoc) { + XDOC *docs = sqlite3_realloc(xm->docs, xt->sdoc + + 128 * sizeof (XDOC)); + + if (!docs) { + sqlite3_mutex_leave(xm->mutex); + goto nomem; + } + xm->docs = docs; + docs += xt->sdoc; + memset(docs, 0, 128 * sizeof (XDOC)); + xt->sdoc += 128; + } + for (i = 0; i < xm->sdoc; i++) { + if (!xm->docs[i].doc) { + xm->docs[i].doc = doc; + xm->docs[i].refcnt = 1; + xm->ndoc++; + xt->idocs[xt->ndoc++] = i; + *rowidp = (sqlite3_int64) (i + 1); + doc = docToFree = 0; + rc = SQLITE_OK; + break; + } + } + } else { + /* UPDATE */ + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("UPDATE not supported"); + } +done: + if (docToFree) { + xmlFreeDoc(docToFree); + } else if (doc && (n >= 0)) { + sqlite3_mutex_enter(xm->mutex); + xm->docs[n].refcnt -= 1; + if (xm->docs[n].refcnt <= 0) { + xm->docs[n].doc = 0; + xm->docs[n].refcnt = 0; + xm->ndoc--; + xmlFreeDoc(doc); + } + sqlite3_mutex_leave(xm->mutex); + } + return rc; +nomem: + if (vtab->zErrMsg) { + sqlite3_free(vtab->zErrMsg); + } + vtab->zErrMsg = sqlite3_mprintf("out of memory"); + rc = SQLITE_NOMEM; + goto done; +} + +/** + * Common XPath select function for virtual table. + * @param ctx SQLite function context + * @param conv conversion (0=string, 1=boolean, 2=number) + * @param argc number of arguments + * @param argv argument vector + * + * Examples: + * + * CREATE VIRTUAL TABLE X USING xpath();<br> + * INSERT INTO X(XML) VALUES('xml-string ...');<br> + * SELECT xpath_string(docid, '//book/title') FROM X;<br> + * SELECT xpath_number(docid, '//book/price') FROM X;<br> + * + * The RHS of the xpath_* functions should be a constant string. + */ + +static void +xpath_vfunc_common(sqlite3_context *ctx, int conv, int argc, + sqlite3_value **argv) +{ + XTAB *xt = (XTAB *) sqlite3_user_data(ctx); + XMOD *xm = xt->xm; + XCSR *xc = xt->xc; + XEXP *xp; + xmlXPathContextPtr pctx = 0; + xmlXPathObjectPtr pobj = 0; + int n; + char *p; + + if ((argc < 2) || !sqlite3_value_text(argv[1])) { + sqlite3_result_error(ctx, "wrong arguments", -1); + goto done; + } + if (!xc) { + sqlite3_result_error(ctx, "not in virtual table context", -1); + goto done; + } + if ((xc->pos < 0) || (xc->pos >= xt->ndoc)) { + sqlite3_result_error(ctx, "cursor out of bounds", -1); + goto done; + } + n = xt->idocs[xc->pos]; + if (!xm->docs[n].doc) { + sqlite3_result_error(ctx, "no docid", -1); + goto done; + } + p = (char *) sqlite3_value_text(argv[1]); + if (!p || !p[0]) { + sqlite3_result_error(ctx, "no or empty XPath expression", -1); + goto done; + } + xp = xc->first; + while (xp) { + if (!strcmp(p, xp->expr)) { + break; + } + xp = xp->next; + } + if (!xp) { + xp = sqlite3_malloc(sizeof (XEXP) + strlen(p)); + if (!xp) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + xp->next = xp->prev = 0; + strcpy(xp->expr, p); + pctx = xmlXPathNewContext(xm->docs[n].doc); + if (!pctx) { + sqlite3_free(xp); + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + pobj = xmlXPathEvalExpression((xmlChar *) xp->expr, pctx); + if (!pobj) { + sqlite3_free(xp); + sqlite3_result_error(ctx, "bad XPath expression", -1); + goto done; + } + xp->doc = xm->docs[n].doc; + xp->pctx = pctx; + xp->pobj = pobj; + xp->parent = 0; + xp->pos = -1; + xp->conv = conv; + pctx = 0; + pobj = 0; + xc->nexpr++; + if (xc->first) { + xc->last->next = xp; + xp->prev = xc->last; + xc->last = xp; + } else { + xc->first = xc->last = xp; + } + } else if (xm->docs[n].doc != xp->doc) { + if (xp->pobj) { + xmlXPathFreeObject(xp->pobj); + xp->pobj = 0; + } + if (xp->pctx) { + xmlXPathFreeContext(xp->pctx); + xp->pctx = 0; + } + xp->doc = xm->docs[n].doc; + xp->parent = 0; + xp->pos = -1; + if (xp->doc) { + pctx = xmlXPathNewContext(xm->docs[n].doc); + if (!pctx) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + pobj = xmlXPathEvalExpression((xmlChar *) xp->expr, pctx); + if (!pobj) { + sqlite3_result_error(ctx, "bad XPath expression", -1); + goto done; + } + xp->pctx = pctx; + xp->pobj = pobj; + pctx = 0; + pobj = 0; + } + } + if (xp->pos < 0) { + xp->pos = 0; + } + if (!xp->pobj) { + xp->parent = 0; + sqlite3_result_null(ctx); + goto done; + } + if ((xp->pobj->type == XPATH_NODESET) && xp->pobj->nodesetval) { + if ((xp->pos < 0) || (xp->pos >= xp->pobj->nodesetval->nodeNr)) { + xp->parent = 0; + sqlite3_result_null(ctx); + } else { + xmlNodePtr node = xp->pobj->nodesetval->nodeTab[xp->pos]; + xmlBufferPtr buf = 0; + + xp->parent = node->parent; + if (node) { + switch (xp->conv) { + case 1: + p = (char *) xmlXPathCastNodeToString(node); + n = xmlXPathCastStringToBoolean((xmlChar *) p); + sqlite3_result_int(ctx, n); + if (p) { + xmlFree(p); + } + break; + case 2: + sqlite3_result_double(ctx, + xmlXPathCastNodeToNumber(node)); + break; + case 3: + buf = xmlBufferCreate(); + if (!buf) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + xmlNodeDump(buf, xp->doc, node, 0, 0); + sqlite3_result_text(ctx, (char *) xmlBufferContent(buf), + xmlBufferLength(buf), + SQLITE_TRANSIENT); + xmlBufferFree(buf); + break; + default: + p = (char *) xmlXPathCastNodeToString(node); + sqlite3_result_text(ctx, p, -1, SQLITE_TRANSIENT); + if (p) { + xmlFree(p); + } + break; + } + } else { + sqlite3_result_null(ctx); + } + } + } else { + xp->parent = 0; + switch (xp->conv) { + case 1: + sqlite3_result_int(ctx, xmlXPathCastToBoolean(xp->pobj)); + break; + case 2: + sqlite3_result_double(ctx, xmlXPathCastToNumber(xp->pobj)); + break; + default: + p = (char *) xmlXPathCastToString(xp->pobj); + sqlite3_result_text(ctx, p, -1, SQLITE_TRANSIENT); + if (p) { + xmlFree(p); + } + break; + } + } +done: + if (pobj) { + xmlXPathFreeObject(pobj); + } + if (pctx) { + xmlXPathFreeContext(pctx); + } +} + +/** + * XPath select function returning string value from virtual table. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_vfunc_string(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_vfunc_common(ctx, 0, argc, argv); +} + +/** + * XPath select function returning boolean value from virtual table. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_vfunc_boolean(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_vfunc_common(ctx, 1, argc, argv); +} + +/** + * XPath select function returning number from virtual table. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_vfunc_number(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_vfunc_common(ctx, 2, argc, argv); +} + +/** + * XPath select function returning XML from virtual table. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_vfunc_xml(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_vfunc_common(ctx, 3, argc, argv); +} + +/** + * Find overloaded function on virtual table. + * @param vtab virtual table + * @param nargs number arguments + * @param name function name + * @param pfunc pointer to function (value return) + * @param parg pointer to function's argument (value return) + * @result 0 or 1 + */ + +static int +xpath_findfunc(sqlite3_vtab *vtab, int nargs, const char *name, + void (**pfunc)(sqlite3_context *, int, sqlite3_value **), + void **parg) +{ + if (nargs != 2) { + return 0; + } + if (!strcmp(name, "xpath_string")) { + *pfunc = xpath_vfunc_string; + *parg = vtab; + return 1; + } + if (!strcmp(name, "xpath_boolean")) { + *pfunc = xpath_vfunc_boolean; + *parg = vtab; + return 1; + } + if (!strcmp(name, "xpath_number")) { + *pfunc = xpath_vfunc_number; + *parg = vtab; + return 1; + } + if (!strcmp(name, "xpath_xml")) { + *pfunc = xpath_vfunc_xml; + *parg = vtab; + return 1; + } + return 0; +} + +#if (SQLITE_VERSION_NUMBER > 3004000) + +/** + * Rename virtual table. + * @param newname new name for table + * @result SQLite error code + */ + +static int +xpath_rename(sqlite3_vtab *vtab, const char *newname) +{ + return SQLITE_OK; +} + +#endif + +/** + * SQLite module descriptor. + */ + +static sqlite3_module xpath_mod = { + 1, /* iVersion */ + xpath_create, /* xCreate */ + xpath_connect, /* xConnect */ + xpath_bestindex, /* xBestIndex */ + xpath_disconnect, /* xDisconnect */ + xpath_destroy, /* xDestroy */ + xpath_open, /* xOpen */ + xpath_close, /* xClose */ + xpath_filter, /* xFilter */ + xpath_next, /* xNext */ + xpath_eof, /* xEof */ + xpath_column, /* xColumn */ + xpath_rowid, /* xRowid */ + xpath_update, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + xpath_findfunc, /* xFindFunction */ +#if (SQLITE_VERSION_NUMBER > 3004000) + xpath_rename, /* xRename */ +#endif +}; + +/** + * Common XPath select function. + * @param ctx SQLite function context + * @param conv conversion (0=string, 1=boolean, 2=number) + * @param argc number of arguments + * @param argv argument vector + * + * Examples: + * + * SELECT xpath_string(<docid>, '//book/title');<br> + * SELECT xpath_number(<xml-string>, '//book/price', + * <options;>, <encoding>, + * <base-url>);<br> + * + * The <docid> argument is the DOCID value of a row + * in a virtual table. Otherwise a string containing an + * XML document is expected. The optional arguments are<br> + * <options> - parser options, see libxml's XML_PARSE_* defines<br> + * <encoding> - encoding of the XML document<br> + * <base-url> - base URL of the XML document<br> + */ + +static void +xpath_func_common(sqlite3_context *ctx, int conv, + int argc, sqlite3_value **argv) +{ + xmlDocPtr doc = 0, docToFree = 0; + xmlXPathContextPtr pctx = 0; + xmlXPathObjectPtr pobj = 0; + XMOD *xm = (XMOD *) sqlite3_user_data(ctx); + int index = 0; + char *p; + + if (argc < 2) { + sqlite3_result_null(ctx); + goto done; + } + if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) { + index = sqlite3_value_int(argv[0]); + if (!xm->mutex) { + sqlite3_result_error(ctx, "init error", -1); + goto done; + } + sqlite3_mutex_enter(xm->mutex); + if ((index <= 0) || (index > xm->sdoc) || !xm->docs[index - 1].doc) { + sqlite3_mutex_leave(xm->mutex); + sqlite3_result_error(ctx, "invalid DOCID", -1); + goto done; + } + doc = xm->docs[index - 1].doc; + xm->docs[index - 1].refcnt += 1; + sqlite3_mutex_leave(xm->mutex); + } else { + int opts = (XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET); + char *enc = 0, *url = 0; + + p = (char *) sqlite3_value_blob(argv[0]); + if (!p) { + sqlite3_result_null(ctx); + return; + } + if ((argc > 2) && (sqlite3_value_type(argv[2]) != SQLITE_NULL)) { + opts = sqlite3_value_int(argv[2]); + } + if ((argc > 3) && (sqlite3_value_type(argv[3]) != SQLITE_NULL)) { + enc = (char *) sqlite3_value_text(argv[3]); + } + if ((argc > 4) && (sqlite3_value_type(argv[4]) != SQLITE_NULL)) { + url = (char *) sqlite3_value_text(argv[4]); + } + doc = xmlReadMemory(p, sqlite3_value_bytes(argv[0]), + url ? url : "", enc, opts); + docToFree = doc; + if (!doc) { + sqlite3_result_error(ctx, "read error", -1); + goto done; + } + } + p = (char *) sqlite3_value_text(argv[1]); + if (!p) { + sqlite3_result_null(ctx); + goto done; + } + pctx = xmlXPathNewContext(doc); + if (!pctx) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + pobj = xmlXPathEvalExpression((xmlChar *) p, pctx); + if (!pobj) { + sqlite3_result_error(ctx, "bad XPath expression", -1); + goto done; + } + switch (conv) { + case 1: + sqlite3_result_int(ctx, xmlXPathCastToBoolean(pobj)); + break; + case 2: + sqlite3_result_double(ctx, xmlXPathCastToNumber(pobj)); + break; + case 3: + if ((pobj->type == XPATH_NODESET) && pobj->nodesetval && + (pobj->nodesetval->nodeNr)) { + xmlNodePtr node = pobj->nodesetval->nodeTab[0]; + xmlBufferPtr buf = 0; + + buf = xmlBufferCreate(); + if (!buf) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + xmlNodeDump(buf, doc, node, 0, 0); + sqlite3_result_text(ctx, (char *) xmlBufferContent(buf), + xmlBufferLength(buf), SQLITE_TRANSIENT); + xmlBufferFree(buf); + } else { + sqlite3_result_null(ctx); + } + break; + default: + p = (char *) xmlXPathCastToString(pobj); + sqlite3_result_text(ctx, p, -1, SQLITE_TRANSIENT); + if (p) { + xmlFree(p); + } + break; + } +done: + if (pobj) { + xmlXPathFreeObject(pobj); + } + if (pctx) { + xmlXPathFreeContext(pctx); + } + if (docToFree) { + xmlFreeDoc(docToFree); + } else if (doc) { + if (xm->mutex) { + sqlite3_mutex_enter(xm->mutex); + if (xm->docs && index) { + xm->docs[index - 1].refcnt -= 1; + if (xm->docs[index - 1].refcnt <= 0) { + docToFree = doc; + xm->docs[index - 1].refcnt = 0; + xm->docs[index - 1].doc = 0; + } + } + sqlite3_mutex_leave(xm->mutex); + if (docToFree) { + xmlFreeDoc(docToFree); + } + } + } +} + +/** + * XPath select function returning string value. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_func_string(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_func_common(ctx, 0, argc, argv); +} + +/** + * XPath select function returning boolean value. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_func_boolean(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_func_common(ctx, 1, argc, argv); +} + +/** + * XPath select function returning number. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_func_number(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_func_common(ctx, 2, argc, argv); +} + +/** + * XPath select function returning XML. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +xpath_func_xml(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xpath_func_common(ctx, 3, argc, argv); +} + +/** + * Function to dump XML document. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Examples: + * + * SELECT xml_dump(<docid>, <encoding> <fmt>)<br> + * + * The <docid> argument is the DOCID value of a row + * in a virtual table. The <encoding> argument is + * the optional encoding (default UTF-8). The <fmt> + * argument is the optional formatting flag for the + * xmlDocDumpFormatMemoryEnc() libxml2 function. + */ + +static void +xpath_func_dump(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + XMOD *xm = (XMOD *) sqlite3_user_data(ctx); + int index = 0, dump_len = 0, fmt = 1; + xmlChar *dump = 0; + char *enc = "utf-8"; + + if (argc < 1) { + sqlite3_result_null(ctx); + return; + } + index = sqlite3_value_int(argv[0]); + if (argc > 1) { + enc = (char *) sqlite3_value_text(argv[1]); + if (!enc) { + enc = "utf-8"; + } + } + if (argc > 2) { + fmt = sqlite3_value_int(argv[2]); + } + if (!xm->mutex) { + sqlite3_result_error(ctx, "init error", -1); + return; + } + sqlite3_mutex_enter(xm->mutex); + if ((index <= 0) || (index > xm->sdoc) || !xm->docs[index - 1].doc) { + sqlite3_mutex_leave(xm->mutex); + sqlite3_result_error(ctx, "invalid DOCID", -1); + return; + } + xmlDocDumpFormatMemoryEnc(xm->docs[index - 1].doc, &dump, &dump_len, + enc, fmt); + if (dump) { + sqlite3_result_text(ctx, (char *) dump, dump_len, SQLITE_TRANSIENT); + xmlFree(dump); + } + sqlite3_mutex_leave(xm->mutex); +} + +#ifdef WITH_XSLT +/** + * Function to transform XML document using XSLT stylesheet. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Examples: + * + * SELECT xslt_transform(<docid>, <stylesheet>, ...)<br> + * SELECT xslt_transform(<xml-string>, <stylesheet>, + * <options;>, <encoding>, + * <base-url>, ...);<br> + * + * The <docid> argument is the DOCID value of a row + * in a virtual table. <stylesheet> is the stylesheet + * to apply on that document. When the transformation succeeded, + * the transformed document replaces the original document.<br> + * In the second form an XML string is transformed with the + * same parameters as in the xpath_string() SQLite function.<br> + * The ellipsis optional arguments are the string parameters for + * the XSLT transformation. + */ + +static void +xpath_func_transform(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + xmlDocPtr doc = 0, docToFree = 0, res = 0; + xsltStylesheetPtr cur = 0; + XMOD *xm = (XMOD *) sqlite3_user_data(ctx); + int index = 0, nparams = 0, param0, i; + char *p; + const char **params = 0; + + if (argc < 2) { + sqlite3_result_null(ctx); + goto done; + } + if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) { + index = sqlite3_value_int(argv[0]); + if (!xm->mutex) { + sqlite3_result_error(ctx, "init error", -1); + goto done; + } + sqlite3_mutex_enter(xm->mutex); + if ((index <= 0) || (index > xm->sdoc) || !xm->docs[index - 1].doc) { + sqlite3_mutex_leave(xm->mutex); + sqlite3_result_error(ctx, "invalid DOCID", -1); + goto done; + } + doc = xm->docs[index - 1].doc; + xm->docs[index - 1].refcnt += 1; + sqlite3_mutex_leave(xm->mutex); + param0 = 2; + nparams = argc - 2; + } else { + int opts = (XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET); + char *enc = 0, *url = 0; + + p = (char *) sqlite3_value_blob(argv[0]); + if (!p) { + sqlite3_result_null(ctx); + return; + } + if ((argc > 2) && (sqlite3_value_type(argv[2]) != SQLITE_NULL)) { + opts = sqlite3_value_int(argv[2]); + } + if ((argc > 3) && (sqlite3_value_type(argv[3]) != SQLITE_NULL)) { + enc = (char *) sqlite3_value_text(argv[3]); + } + if ((argc > 4) && (sqlite3_value_type(argv[4]) != SQLITE_NULL)) { + url = (char *) sqlite3_value_text(argv[4]); + } + doc = xmlReadMemory(p, sqlite3_value_bytes(argv[0]), + url ? url : "", enc, opts); + docToFree = doc; + if (!doc) { + sqlite3_result_error(ctx, "read error", -1); + goto done; + } + param0 = 5; + nparams = argc - 5; + } + p = (char *) sqlite3_value_text(argv[1]); + if (!p) { + sqlite3_result_null(ctx); + goto done; + } + cur = xsltParseStylesheetFile((xmlChar *) p); + if (!cur) { + sqlite3_result_error(ctx, "read error on stylesheet", -1); + goto done; + } + if (nparams <= 0) { + nparams = 1; + } else { + nparams++; + } + params = sqlite3_malloc(nparams * sizeof (char *)); + if (!params) { + sqlite3_result_error(ctx, "out of memory", -1); + goto done; + } + for (i = 0; i < (argc - param0); i++) { + params[i] = (const char *) sqlite3_value_text(argv[i + param0]); + if (!params[i]) { + params[i] = ""; + } + } + params[i] = 0; + res = xsltApplyStylesheet(cur, doc, params); + if (!res) { + sqlite3_result_error(ctx, "transformation failed", -1); + goto done; + } + if (docToFree) { + xmlChar *str = 0; + + xmlFreeDoc(docToFree); + docToFree = res; + i = 0; + xsltSaveResultToString(&str, &i, res, cur); + if (str) { + sqlite3_result_text(ctx, (char *) str, i, SQLITE_TRANSIENT); + xmlFree(str); + } else { + sqlite3_result_null(ctx); + } + } +done: + if (params) { + sqlite3_free(params); + } + if (cur) { + xsltFreeStylesheet(cur); + } + if (docToFree) { + xmlFreeDoc(docToFree); + } else if (doc) { + if (xm->mutex) { + sqlite3_mutex_enter(xm->mutex); + if (xm->docs && index) { + docToFree = doc; + xm->docs[index - 1].doc = 0; + xmlFreeDoc(docToFree); + docToFree = 0; + xm->docs[index - 1].refcnt -= 1; + xm->docs[index - 1].doc = res; + if (xm->docs[index - 1].refcnt <= 0) { + docToFree = res; + xm->docs[index - 1].refcnt = 0; + xm->docs[index - 1].doc = 0; + } + } + sqlite3_mutex_leave(xm->mutex); + if (docToFree) { + xmlFreeDoc(docToFree); + } + } + } +} +#endif + +/** + * Module finalizer. + * @param aux pointer to module data + * @result SQLite error code + */ + +static void +xpath_fini(void *aux) +{ + XMOD *xm = (XMOD *) aux; + XDOC *docs; + int i, n, cleanup = 0; + sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + + if (!mutex) { + return; + } + sqlite3_mutex_enter(mutex); + if (initialized) { + xm->refcnt--; + if (xm->refcnt <= 0) { + xmod = 0; + initialized = 0; + cleanup = 1; + } + } else { + cleanup = 1; + } + sqlite3_mutex_leave(mutex); + if (cleanup) { + sqlite3_mutex_enter(xm->mutex); + mutex = xm->mutex; + xm->mutex = 0; + docs = xm->docs; + n = xm->ndoc; + xm->docs = 0; + xm->sdoc = xm->ndoc = 0; + sqlite3_mutex_leave(mutex); + sqlite3_mutex_free(mutex); + for (i = 0; i < n; i++) { + if (docs->refcnt <= 0) { + xmlFreeDoc(docs->doc); + docs->doc = 0; + } + } + sqlite3_free(docs); + sqlite3_free(xm); + } +} + +/** + * Module initializer creating SQLite module and functions. + * @param db database pointer + * @result SQLite error code + */ + +#ifndef STANDALONE +static +#endif +int +xpath_init(sqlite3 *db) +{ + XMOD *xm; + int rc; + sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + + if (!mutex) { + return SQLITE_NOMEM; + } + sqlite3_mutex_enter(mutex); + if (!initialized) { + xm = sqlite3_malloc(sizeof (XMOD)); + if (!xm) { + sqlite3_mutex_leave(mutex); + return SQLITE_NOMEM; + } + xm->refcnt = 1; + xm->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if (!xm->mutex) { + sqlite3_mutex_leave(mutex); + sqlite3_free(xm); + return SQLITE_NOMEM; + } + xm->sdoc = 128; + xm->ndoc = 0; + xm->docs = sqlite3_malloc(xm->sdoc * sizeof (XDOC)); + if (!xm->docs) { + sqlite3_mutex_leave(mutex); + sqlite3_mutex_free(xm->mutex); + sqlite3_free(xm); + return SQLITE_NOMEM; + } + memset(xm->docs, 0, xm->sdoc * sizeof (XDOC)); + xmod = xm; + initialized = 1; + } else { + xm = xmod; + xm->refcnt++; + } + sqlite3_mutex_leave(mutex); + sqlite3_create_function(db, "xpath_string", -1, SQLITE_UTF8, + (void *) xm, xpath_func_string, 0, 0); + sqlite3_create_function(db, "xpath_boolean", -1, SQLITE_UTF8, + (void *) xm, xpath_func_boolean, 0, 0); + sqlite3_create_function(db, "xpath_number", -1, SQLITE_UTF8, + (void *) xm, xpath_func_number, 0, 0); + sqlite3_create_function(db, "xpath_xml", -1, SQLITE_UTF8, + (void *) xm, xpath_func_xml, 0, 0); + sqlite3_create_function(db, "xml_dump", -1, SQLITE_UTF8, + (void *) xm, xpath_func_dump, 0, 0); +#ifdef WITH_XSLT + sqlite3_create_function(db, "xslt_transform", -1, SQLITE_UTF8, + (void *) xm, xpath_func_transform, 0, 0); +#endif + rc = sqlite3_create_module_v2(db, "xpath", &xpath_mod, + (void *) xm, xpath_fini); + if (rc != SQLITE_OK) { + sqlite3_create_function(db, "xpath_string", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); + sqlite3_create_function(db, "xpath_boolean", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); + sqlite3_create_function(db, "xpath_number", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); + sqlite3_create_function(db, "xpath_xml", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); + sqlite3_create_function(db, "xml_dump", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); +#ifdef WITH_XSLT + sqlite3_create_function(db, "xslt_transform", -1, SQLITE_UTF8, + (void *) xm, 0, 0, 0); +#endif + xpath_fini(xm); + } + return rc; +} + +#ifndef STANDALONE + +/** + * Initializer for SQLite extension load mechanism. + * @param db SQLite database pointer + * @param errmsg pointer receiving error message + * @param api SQLite API routines + * @result SQLite error code + */ + +int +sqlite3_extension_init(sqlite3 *db, char **errmsg, + const sqlite3_api_routines *api) +{ + SQLITE_EXTENSION_INIT2(api); + return xpath_init(db); +} + +#endif diff --git a/lang/sql/odbc/xpath.rc b/lang/sql/odbc/xpath.rc new file mode 100644 index 00000000..936a9a99 --- /dev/null +++ b/lang/sql/odbc/xpath.rc @@ -0,0 +1,48 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite3.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (3) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLite3 Extension XPath\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "XPATH\0" + VALUE "LegalCopyright", "Public Domain\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "SQLITE3_MOD_XPATH.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite3 " SQLITE_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqliteodbc.ico" diff --git a/lang/sql/odbc/zipfile.c b/lang/sql/odbc/zipfile.c new file mode 100644 index 00000000..3d301b1c --- /dev/null +++ b/lang/sql/odbc/zipfile.c @@ -0,0 +1,2308 @@ +/** + * @file zipfile.c + * SQLite extension module for mapping a ZIP file as a read-only + * SQLite virtual table plus some supporting SQLite functions. + * + * 2012 September 12 + * + * The author disclaims copyright to this source code. + * In place of a legal notice, here is a blessing: + * + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + * + ******************************************************************** + */ + +#ifdef linux +#define _GNU_SOURCE +#endif + +#ifdef STANDALONE +#include <sqlite3.h> +#else +#include <sqlite3ext.h> +static SQLITE_EXTENSION_INIT1 +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#else +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include <zlib.h> + +#undef snprintf + +#define ZIP_SIG_LEN 4 + +#define ZIP_LOCAL_HEADER_SIG 0x04034b50 +#define ZIP_LOCAL_HEADER_FLAGS 6 +#define ZIP_LOCAL_PATHLEN_OFFS 26 +#define ZIP_LOCAL_EXTRA_OFFS 28 +#define ZIP_LOCAL_HEADER_LEN 30 + +#define ZIP_CENTRAL_HEADER_SIG 0x02014b50 +#define ZIP_CENTRAL_HEADER_FLAGS 8 +#define ZIP_CENTRAL_HEADER_LEN 46 +#define ZIP_CENTRAL_COMPMETH_OFFS 10 +#define ZIP_CENTRAL_MTIME_OFFS 12 +#define ZIP_CENTRAL_MDATE_OFFS 14 +#define ZIP_CENTRAL_CRC32_OFFS 16 +#define ZIP_CENTRAL_COMPLEN_OFFS 20 +#define ZIP_CENTRAL_UNCOMPLEN_OFFS 24 +#define ZIP_CENTRAL_PATHLEN_OFFS 28 +#define ZIP_CENTRAL_EXTRALEN_OFFS 30 +#define ZIP_CENTRAL_COMMENTLEN_OFFS 32 +#define ZIP_CENTRAL_LOCALHDR_OFFS 42 + +#define ZIP_CENTRAL_END_SIG 0x06054b50 +#define ZIP_CENTRAL_END_LEN 22 +#define ZIP_CENTRAL_ENTS_OFFS 8 +#define ZIP_CENTRAL_DIRSIZE_OFFS 12 +#define ZIP_CENTRAL_DIRSTART_OFFS 16 + +#define ZIP_COMPMETH_STORED 0 +#define ZIP_COMPMETH_DEFLATED 8 + +#define zip_read_int(p) \ + ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)) +#define zip_read_short(p) \ + ((p)[0] | ((p)[1] << 8)) + +/** + * @typedef zip_file + * @struct zip_file + * Structure to implement ZIP file handle. + */ + +typedef struct zip_file { + off_t length; /**< length of ZIP file */ + unsigned char *data; /**< mmap()'ed ZIP file */ +#if defined(_WIN32) || defined(_WIN64) + HANDLE h; /**< Windows file handle */ + HANDLE mh; /**< Windows file mapping */ +#endif + int baseoffs; /**< Global offset for embedded ZIP files */ + int nentries; /**< Number of directory entries */ + unsigned char *entries[1]; /**< Pointer to first entry */ +} zip_file; + +/** + * @typedef zip_vtab + * @struct zip_vtab + * Structure to describe a ZIP virtual table. + */ + +typedef struct zip_vtab { + sqlite3_vtab vtab; /**< SQLite virtual table */ + sqlite3 *db; /**< Open database */ + zip_file *zip; /**< ZIP file handle */ + int sorted; /**< 1 = sorted by path, -1 = sorting, 0 = unsorted */ + char tblname[1]; /**< Name, format "database"."table" */ +} zip_vtab; + +/** + * @typedef zip_cursor + * @struct zip_cursor + * Structure to describe ZIP virtual table cursor. + */ + +typedef struct { + sqlite3_vtab_cursor cursor; /**< SQLite virtual table cursor */ + int pos; /**< ZIP file position */ + int usematches; /**< For filter EQ */ + int nmatches; /**< For filter EQ */ + int *matches; /**< For filter EQ */ +} zip_cursor; + +#ifdef SQLITE_OPEN_URI + +/** + * @typedef mem_blk + * @struct mem_blk + * Structure to describe in-core SQLite database read from BLOB. + */ + +typedef struct mem_blk { +#define MEM_MAGIC "MVFS" + char magic[4]; /**< magic number */ + int opened; /**< open counter */ +#if defined(_WIN32) || defined(_WIN64) + HANDLE mh; /**< handle for memory mapping */ +#else + long psize; /**< page size */ +#ifdef linux + sqlite3_mutex *mutex; /**< mutex to protect mapping */ + int lcnt; /**< lock counter */ +#endif +#endif + unsigned long size; /**< size of memory mapped area */ + unsigned long length; /**< real length of data area */ + unsigned char *data; /**< data area */ +} mem_blk; + +/** + * @typedef mem_file + * @struct mem_file + * SQLite3 file structure enhanced by mem_blk. + */ + +typedef struct mem_file { + sqlite3_file base; /**< sqlite3_file base structure */ +#ifdef linux + int lock; /**< lock state */ +#endif + mem_blk *mb; /**< pointer to memory block */ +} mem_file; + +/* + * Private VFS name + */ + +static char mem_vfs_name[64]; + +#endif /* SQLITE_OPEN_URI */ + +/** + * Memory map ZIP file for reading and return handle to it. + * @param filename name of ZIP file + * @result ZIP file handle + */ + +static zip_file * +zip_open(const char *filename) +{ +#if defined(_WIN32) || defined(_WIN64) + HANDLE h, mh = INVALID_HANDLE_VALUE; + DWORD length; + unsigned char *data = 0; +#else + int fd; + off_t length; + unsigned char *data = MAP_FAILED; +#endif + int nentries, baseoffs = 0, i; + zip_file *zip = 0; + unsigned char *p, *q; + + if (!filename) { + return 0; + } +#if defined(_WIN32) || defined(_WIN64) + h = CreateFile(filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); + if (h == INVALID_HANDLE_VALUE) { + goto error; + } + length = GetFileSize(h, 0); + if ((length == INVALID_FILE_SIZE) || (length < ZIP_CENTRAL_END_LEN)) { + goto error; + } + mh = CreateFileMapping(h, 0, PAGE_READONLY, 0, length, 0); + if (mh == INVALID_HANDLE_VALUE) { + goto error; + } + data = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, length); + if (!data) { + goto error; + } +#else + fd = open(filename, O_RDONLY); + if (fd < 0) { + goto error; + } + length = lseek(fd, 0, SEEK_END); + if ((length == -1) || (length < ZIP_CENTRAL_END_LEN)) { + goto error; + } + data = (unsigned char *) mmap(0, length, PROT_READ, +#ifdef MAP_FILE + MAP_FILE | +#endif + MAP_PRIVATE, fd, 0); + if (data == MAP_FAILED) { + goto error; + } + close(fd); + fd = -1; +#endif + p = data + length - ZIP_CENTRAL_END_LEN; + while (p >= data) { + if (*p == (ZIP_CENTRAL_END_SIG & 0xFF)) { + if (zip_read_int(p) == ZIP_CENTRAL_END_SIG) { + break; + } + p -= ZIP_SIG_LEN; + } else { + --p; + } + } + if (p < data) { + goto error; + } + nentries = zip_read_short(p + ZIP_CENTRAL_ENTS_OFFS); + if (nentries == 0) { + goto error; + } + q = data + zip_read_int(p + ZIP_CENTRAL_DIRSTART_OFFS); + p -= zip_read_int(p + ZIP_CENTRAL_DIRSIZE_OFFS); + if (p < data || p > data + length || q < data || q > data + length) { + goto error; + } + baseoffs = p - q; + q = p; + for (i = 0; i < nentries; i++) { + int pathlen, comlen, extra; + + if ((q + ZIP_CENTRAL_HEADER_LEN) > (data + length)) { + goto error; + } + if (zip_read_int(q) != ZIP_CENTRAL_HEADER_SIG) { + goto error; + } + pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS); + comlen = zip_read_short(q + ZIP_CENTRAL_COMMENTLEN_OFFS); + extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS); + q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN; + } + zip = sqlite3_malloc(sizeof (zip_file) + + nentries * sizeof (unsigned char *)); + if (!zip) { + goto error; + } +#if defined(_WIN32) || defined(_WIN64) + zip->h = zip->mh = INVALID_HANDLE_VALUE; +#endif + zip->length = length; + zip->data = data; + zip->baseoffs = baseoffs; + zip->nentries = nentries; + q = p; + for (i = 0; i < nentries; i++) { + int pathlen, comlen, extra; + + if ((q + ZIP_CENTRAL_HEADER_LEN) > (data + length)) { + goto error; + } + if (zip_read_int(q) != ZIP_CENTRAL_HEADER_SIG) { + goto error; + } + zip->entries[i] = q; + pathlen = zip_read_short(q + ZIP_CENTRAL_PATHLEN_OFFS); + comlen = zip_read_short(q + ZIP_CENTRAL_COMMENTLEN_OFFS); + extra = zip_read_short(q + ZIP_CENTRAL_EXTRALEN_OFFS); + q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN; + } + zip->entries[i] = 0; +#if defined(_WIN32) || defined(_WIN64) + zip->h = h; + zip->mh = mh; +#endif + return zip; +error: + if (zip) { + sqlite3_free(zip); + } +#if defined(_WIN32) || defined(_WIN64) + if (data) { + UnmapViewOfFile(data); + } + if (mh != INVALID_HANDLE_VALUE) { + CloseHandle(mh); + } + if (h != INVALID_HANDLE_VALUE) { + CloseHandle(h); + } +#else + if (data != MAP_FAILED) { + munmap(data, length); + } + if (fd >= 0) { + close(fd); + } +#endif + return 0; +} + +/** + * Close ZIP file handle. + * @param zip ZIP file handle + */ + +static void +zip_close(zip_file *zip) +{ + if (zip) { +#if defined(_WIN32) || defined(_WIN64) + if (zip->data) { + UnmapViewOfFile(zip->data); + } + if (zip->mh != INVALID_HANDLE_VALUE) { + CloseHandle(zip->mh); + } + if (zip->h != INVALID_HANDLE_VALUE) { + CloseHandle(zip->h); + } +#else + if (zip->data) { + munmap(zip->data, zip->length); + } +#endif + zip->length = 0; + zip->data = 0; + zip->nentries = 0; + sqlite3_free(zip); + } +} + +/** + * Strip off quotes given string. + * @param in string to be processed + * @result new string to be free'd with sqlite3_free() + */ + +static char * +unquote(char const *in) +{ + char c, *ret; + int i; + + ret = sqlite3_malloc(strlen(in) + 1); + if (ret) { + c = in[0]; + if ((c == '"') || (c == '\'')) { + i = strlen(in + 1); + if ((i > 0) && (in[i] == c)) { + strcpy(ret, in + 1); + ret[i - 1] = '\0'; + return ret; + } + } + strcpy(ret, in); + } + return ret; +} + +/** + * Connect to virtual table + * @param db SQLite database pointer + * @param aux user specific pointer (unused) + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + * + * Argument vector contains: + * + * argv[0] - module name<br> + * argv[1] - database name<br> + * argv[2] - table name (virtual table)<br> + * argv[3] - filename of ZIP file<br> + */ + +static int +zip_vtab_connect(sqlite3* db, void *aux, int argc, const char * const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + zip_file *zip = 0; + int rc = SQLITE_ERROR; + char *filename; + zip_vtab *vtab; + + if (argc < 4) { + *errp = sqlite3_mprintf("input file name missing"); + return SQLITE_ERROR; + } + filename = unquote(argv[3]); + if (filename) { + zip = zip_open(filename); + sqlite3_free(filename); + } + if (!zip) { + *errp = sqlite3_mprintf("unable to open input file"); + return rc; + } + vtab = sqlite3_malloc(sizeof(zip_vtab) + 6 + + strlen(argv[1]) + strlen(argv[2])); + if (!vtab) { + zip_close(zip); + *errp = sqlite3_mprintf("out of memory"); + return rc; + } + memset(vtab, 0, sizeof (*vtab)); + strcpy(vtab->tblname, "\""); + strcat(vtab->tblname, argv[1]); + strcat(vtab->tblname, "\".\""); + strcat(vtab->tblname, argv[2]); + strcat(vtab->tblname, "\""); + vtab->db = db; + vtab->zip = zip; + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(path, comp, mtime, " + "crc32, length, data, clength, cdata, isdir)"); + if (rc != SQLITE_OK) { + zip_close(zip); + sqlite3_free(vtab); + *errp = sqlite3_mprintf("table definition failed (error %d)", rc); + return rc; + } + *vtabp = &vtab->vtab; + *errp = 0; + return SQLITE_OK; +} + +/** + * Create virtual table + * @param db SQLite database pointer + * @param aux user specific pointer (unused) + * @param argc argument count + * @param argv argument vector + * @param vtabp pointer receiving virtual table pointer + * @param errp pointer receiving error messag + * @result SQLite error code + */ + +static int +zip_vtab_create(sqlite3* db, void *aux, int argc, + const char *const *argv, + sqlite3_vtab **vtabp, char **errp) +{ + return zip_vtab_connect(db, aux, argc, argv, vtabp, errp); +} + +/** + * Disconnect virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +zip_vtab_disconnect(sqlite3_vtab *vtab) +{ + zip_vtab *tab = (zip_vtab *) vtab; + + zip_close(tab->zip); + sqlite3_free(tab); + return SQLITE_OK; +} + +/** + * Destroy virtual table. + * @param vtab virtual table pointer + * @result always SQLITE_OK + */ + +static int +zip_vtab_destroy(sqlite3_vtab *vtab) +{ + return zip_vtab_disconnect(vtab); +} + +/** + * Determines information for filter function according to constraints. + * @param vtab virtual table + * @param info index/constraint information + * @result SQLite error code + */ + +static int +zip_vtab_bestindex(sqlite3_vtab *vtab, sqlite3_index_info *info) +{ + zip_vtab *tab = (zip_vtab *) vtab; + int i; + + info->idxNum = 0; + if (tab->sorted == 0) { + char *sql = 0; + unsigned char **entries = 0; + sqlite3_stmt *stmt = 0; + int rc, count, i; + size_t tmp; + + /* perform sorting on 0th column (path, string) */ + tab->sorted = -1; + entries = sqlite3_malloc(tab->zip->nentries * sizeof (entries)); + sql = sqlite3_mprintf("SELECT rowid FROM %s ORDER BY path", + tab->tblname); + if (sql && entries) { + rc = sqlite3_prepare_v2(tab->db, sql, -1, &stmt, 0); + if ((rc == SQLITE_OK) && stmt) { + count = 0; + while (1) { + rc = sqlite3_step(stmt); + if (rc != SQLITE_ROW) { + break; + } + tmp = sqlite3_column_int(stmt, 0); + entries[count++] = (unsigned char *) tmp; + } + if ((rc == SQLITE_DONE) && (count == tab->zip->nentries)) { + for (i = 0; i < count; i++) { + tmp = (size_t) entries[i]; + tmp = (size_t) tab->zip->entries[tmp]; + entries[i] = (unsigned char *) tmp; + } + memcpy(tab->zip->entries, entries, i * sizeof (entries)); + tab->sorted = 1; + } + } + } + if (stmt) { + sqlite3_finalize(stmt); + } + if (sql) { + sqlite3_free(sql); + } + if (entries) { + sqlite3_free(entries); + } + } + /* no optimization while table is being sorted or is unsorted */ + if (tab->sorted != 1) { + return SQLITE_OK; + } + /* support EQ or simple MATCH constraint on 0th column (path) */ + for (i = 0; i < info->nConstraint; i++) { + if (info->aConstraint[i].usable && + (info->aConstraint[i].iColumn == 0)) { + if (info->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ) { + info->idxNum = 1; + info->aConstraintUsage[i].argvIndex = 1; + info->aConstraintUsage[i].omit = 1; + info->estimatedCost = 1.0; + break; + } else if (info->aConstraint[i].op == + SQLITE_INDEX_CONSTRAINT_MATCH) { + info->idxNum = 2; + info->aConstraintUsage[i].argvIndex = 1; + info->aConstraintUsage[i].omit = 1; + info->estimatedCost = 2.0; + break; + } + } + } + /* ORDER BY is ascending on 0th column (path) when table is sorted */ + if (info->nOrderBy > 0) { + if ((info->aOrderBy[0].iColumn == 0) && !info->aOrderBy[0].desc) { + info->orderByConsumed = 1; + } + } + return SQLITE_OK; +} + +/** + * Open virtual table and return cursor. + * @param vtab virtual table pointer + * @param cursorp pointer receiving cursor pointer + * @result SQLite error code + */ + +static int +zip_vtab_open(sqlite3_vtab *vtab, sqlite3_vtab_cursor **cursorp) +{ + zip_cursor *cur = sqlite3_malloc(sizeof(*cur)); + + if (!cur) { + return SQLITE_ERROR; + } + cur->cursor.pVtab = vtab; + cur->pos = -1; + cur->usematches = 0; + cur->nmatches = 0; + cur->matches = 0; + *cursorp = &cur->cursor; + return SQLITE_OK; +} + +/** + * Close virtual table cursor. + * @param cursor cursor pointer + * @result SQLite error code + */ + +static int +zip_vtab_close(sqlite3_vtab_cursor *cursor) +{ + zip_cursor *cur = (zip_cursor *) cursor; + + if (cur->matches) { + sqlite3_free(cur->matches); + } + sqlite3_free(cur); + return SQLITE_OK; +} + +/** + * Retrieve next row from virtual table cursor + * @param cursor virtual table cursor + * @result SQLite error code + */ + +static int +zip_vtab_next(sqlite3_vtab_cursor *cursor) +{ + zip_cursor *cur = (zip_cursor *) cursor; + + if (cur->nmatches >= 0) { + cur->pos++; + } + return SQLITE_OK; +} + +/** + * Filter function for virtual table. + * @param cursor virtual table cursor + * @param idxNum used for expression (1 -> EQ, 2 -> MATCH, 0 else) + * @param idxStr nod used + * @param argc number arguments (1 -> EQ/MATCH, 0 else) + * @param argv argument (nothing or RHS of filter expression) + * @result SQLite error code + */ + +static int +zip_vtab_filter(sqlite3_vtab_cursor *cursor, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) +{ + zip_cursor *cur = (zip_cursor *) cursor; + zip_vtab *tab = (zip_vtab *) cur->cursor.pVtab; + + if (cur->matches) { + sqlite3_free(cur->matches); + cur->matches = 0; + } + cur->usematches = 0; + cur->nmatches = 0; + /* if EQ or MATCH constraint is active, add match array to cursor */ + if (idxNum && (argc > 0)) { + int i, k, d, found, leneq, len; + unsigned char *eq; + + eq = (unsigned char *) sqlite3_value_text(argv[0]); + if (!eq) { + cur->nmatches = -1; + goto done; + } + if (idxNum > 1) { + unsigned char *p = (unsigned char *) strrchr((char *) eq, '*'); + + if (!p || (p[1] != '\0')) { + return SQLITE_ERROR; + } + leneq = p - eq; + } else { + leneq = sqlite3_value_bytes(argv[0]); + if (leneq == 0) { + cur->nmatches = -1; + goto done; + } + } + cur->matches = sqlite3_malloc(tab->zip->nentries * sizeof (int)); + if (!cur->matches) { + return SQLITE_NOMEM; + } + cur->usematches = 1; + memset(cur->matches, 0, tab->zip->nentries * sizeof (int)); + for (k = found = 0; k < tab->zip->nentries; k++) { + len = zip_read_short(tab->zip->entries[k] + + ZIP_CENTRAL_PATHLEN_OFFS); + if (idxNum > 1) { + if (len < leneq) { + continue; + } + } else if (len != leneq) { + if (found) { + break; + } + continue; + } + d = memcmp(tab->zip->entries[k] + ZIP_CENTRAL_HEADER_LEN, + eq, leneq); + if (d == 0) { + found++; + cur->matches[k] = 1; + } else if (d > 0) { + break; + } + } + for (i = k = 0; i < tab->zip->nentries; i++) { + if (cur->matches[i]) { + cur->matches[k++] = i; + } + } + cur->nmatches = k; + } +done: + cur->pos = -1; + return zip_vtab_next(cursor); +} + +/** + * Return end of table state of virtual table cursor + * @param cursor virtual table cursor + * @result true/false + */ + +static int +zip_vtab_eof(sqlite3_vtab_cursor *cursor) +{ + zip_cursor *cur = (zip_cursor *) cursor; + zip_vtab *tab = (zip_vtab *) cur->cursor.pVtab; + + if (cur->nmatches < 0) { + return 1; + } + if (cur->usematches) { + return cur->pos >= cur->nmatches; + } + return cur->pos >= tab->zip->nentries; +} + +/** + * Return column data of virtual table. + * @param cursor virtual table cursor + * @param ctx SQLite function context + * @param n column index + * @result SQLite error code + */ + +static int +zip_vtab_column(sqlite3_vtab_cursor *cursor, sqlite3_context *ctx, int n) +{ + zip_cursor *cur = (zip_cursor *) cursor; + zip_vtab *tab = (zip_vtab *) cur->cursor.pVtab; + unsigned char *data = 0; + unsigned char *dest = 0; + int length; + + if (cur->usematches) { + int pos; + + if ((cur->pos < 0) || (cur->pos >= cur->nmatches)) { + sqlite3_result_error(ctx, "out of bounds", -1); + return SQLITE_ERROR; + } + pos = cur->matches[cur->pos]; + data = tab->zip->entries[pos]; + } else { + if ((cur->pos < 0) || (cur->pos >= tab->zip->nentries)) { + sqlite3_result_error(ctx, "out of bounds", -1); + return SQLITE_ERROR; + } + data = tab->zip->entries[cur->pos]; + } + switch (n) { + case 0: /* "path": pathname */ + length = zip_read_short(data + ZIP_CENTRAL_PATHLEN_OFFS); + data += ZIP_CENTRAL_HEADER_LEN; + sqlite3_result_text(ctx, (char *) data, length, SQLITE_TRANSIENT); + return SQLITE_OK; + case 1: /* "comp": compression method */ + length = zip_read_short(data + ZIP_CENTRAL_COMPMETH_OFFS); + sqlite3_result_int(ctx, length); + return SQLITE_OK; + case 2: /* "mtime": modification time/date */ + { + int time = zip_read_short(data + ZIP_CENTRAL_MTIME_OFFS); + int date = zip_read_short(data + ZIP_CENTRAL_MDATE_OFFS); + char mtbuf[64]; + + sprintf(mtbuf, "%04d-%02d-%02d %02d:%02d:%02d", + (date >> 9) + 1980, (date >> 5) & 0xf, date & 0x1f, + time >> 11, (time >> 5) & 0x3f, (time & 0x1f) << 1); + sqlite3_result_text(ctx, mtbuf, -1, SQLITE_TRANSIENT); + return SQLITE_OK; + } + case 3: /* "crc32": CRC32 of uncompress data */ + length = zip_read_int(data + ZIP_CENTRAL_CRC32_OFFS); + sqlite3_result_int(ctx, length); + return SQLITE_OK; + case 4: /* "length": uncompress length of data */ + length = zip_read_int(data + ZIP_CENTRAL_UNCOMPLEN_OFFS); + sqlite3_result_int(ctx, length); + return SQLITE_OK; + case 5: /* "data": uncompressed data */ + { + int clength, offs, extra, pathlen, cmeth; + + offs = tab->zip->baseoffs + + zip_read_int(data + ZIP_CENTRAL_LOCALHDR_OFFS); + if ((offs + ZIP_LOCAL_HEADER_LEN) > tab->zip->length) { + goto donull; + } + extra = zip_read_short(tab->zip->data + offs + + ZIP_LOCAL_EXTRA_OFFS); + pathlen = zip_read_short(tab->zip->data + offs + + ZIP_LOCAL_PATHLEN_OFFS); + length = zip_read_int(data + ZIP_CENTRAL_UNCOMPLEN_OFFS); + clength = zip_read_int(data + ZIP_CENTRAL_COMPLEN_OFFS); + cmeth = zip_read_short(data + ZIP_CENTRAL_COMPMETH_OFFS); + offs += ZIP_LOCAL_HEADER_LEN + pathlen + extra; + if ((offs + clength) > tab->zip->length) { + goto donull; + } + data = tab->zip->data + offs; + if (cmeth == ZIP_COMPMETH_STORED) { + sqlite3_result_blob(ctx, data, clength, SQLITE_TRANSIENT); + return SQLITE_OK; + } else if (cmeth == ZIP_COMPMETH_DEFLATED) { + z_stream stream; + int err; + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.next_in = data; + stream.avail_in = clength; + stream.next_out = dest = sqlite3_malloc(length); + stream.avail_out = length; + stream.opaque = 0; + if (!dest) { + goto donull; + } + if (inflateInit2(&stream, -15) != Z_OK) { + goto donull; + } + err = inflate(&stream, Z_SYNC_FLUSH); + inflateEnd(&stream); + if ((err == Z_STREAM_END) || + ((err == Z_OK) && (stream.avail_in == 0))) { + sqlite3_result_blob(ctx, dest, length, sqlite3_free); + return SQLITE_OK; + } + } + donull: + if (dest) { + sqlite3_free(dest); + } + sqlite3_result_null(ctx); + return SQLITE_OK; + } + case 6: /* "clength": compressed length */ + length = zip_read_int(data + ZIP_CENTRAL_COMPLEN_OFFS); + sqlite3_result_int(ctx, length); + return SQLITE_OK; + case 7: /* "cdata": raw data */ + { + int clength, offs, extra, pathlen; + + offs = tab->zip->baseoffs + + zip_read_int(data + ZIP_CENTRAL_LOCALHDR_OFFS); + if ((offs + ZIP_LOCAL_HEADER_LEN) > tab->zip->length) { + goto donull; + } + extra = zip_read_short(tab->zip->data + offs + + ZIP_LOCAL_EXTRA_OFFS); + pathlen = zip_read_short(tab->zip->data + offs + + ZIP_LOCAL_PATHLEN_OFFS); + length = zip_read_int(data + ZIP_CENTRAL_UNCOMPLEN_OFFS); + clength = zip_read_int(data + ZIP_CENTRAL_COMPLEN_OFFS); + offs += ZIP_LOCAL_HEADER_LEN + pathlen + extra; + if ((offs + clength) > tab->zip->length) { + goto donull; + } + data = tab->zip->data + offs; + sqlite3_result_blob(ctx, data, clength, SQLITE_TRANSIENT); + return SQLITE_OK; + } + case 8: /* "isdir": directory indicator */ + length = zip_read_short(data + ZIP_CENTRAL_PATHLEN_OFFS); + data += ZIP_CENTRAL_HEADER_LEN; + sqlite3_result_int(ctx, (length > 0 && data[length - 1] == '/')); + return SQLITE_OK; + } + sqlite3_result_error(ctx, "invalid column number", -1); + return SQLITE_ERROR; +} + +/** + * Return current rowid of virtual table cursor + * @param cursor virtual table cursor + * @param rowidp value buffer to receive current rowid + * @result SQLite error code + */ + +static int +zip_vtab_rowid(sqlite3_vtab_cursor *cursor, sqlite_int64 *rowidp) +{ + zip_cursor *cur = (zip_cursor *) cursor; + + if (cur->nmatches < 0) { + *rowidp = -1; + } else if ((cur->pos >= 0) && (cur->usematches > 0)) { + if (cur->pos < cur->nmatches) { + *rowidp = cur->matches[cur->pos]; + } else { + *rowidp = -1; + } + } else { + *rowidp = cur->pos; + } + return SQLITE_OK; +} + +/** + * Internal MATCH function for virtual table. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +zip_vtab_matchfunc(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + int ret = 0; + + if (argc == 2) { + unsigned char *q = (unsigned char *) sqlite3_value_text(argv[0]); + unsigned char *p = (unsigned char *) sqlite3_value_text(argv[1]); + + if (p && q) { + unsigned char *eq = (unsigned char *) strrchr((char *) q, '*'); + int lenq, lenp; + + if (eq && (eq[1] == '\0')) { + lenq = eq - q; + if (lenq) { + lenp = strlen((char *) p); + if ((lenp >= lenq) && !memcmp(p, q, lenq)) { + ret = 1; + } + } + } + } + } + sqlite3_result_int(ctx, ret); +} + +/** + * Find overloaded function on virtual table. + * @param vtab virtual table + * @param narg number arguments + * @param name function name + * @param pfunc pointer to function (value return) + * @param parg pointer to function's argument (value return) + * @result 0 or 1 + */ + +static int +zip_vtab_findfunc(sqlite3_vtab *vtab, int narg, const char *name, + void (**pfunc)(sqlite3_context *, int, sqlite3_value **), + void **parg) +{ + if ((narg == 2) && !strcmp(name, "match")) { + *pfunc = zip_vtab_matchfunc; + *parg = 0; + return 1; + } + return 0; +} + +#if (SQLITE_VERSION_NUMBER > 3004000) + +/** + * Rename virtual table. + * @param newname new name for table + * @result SQLite error code + */ + +static int +zip_vtab_rename(sqlite3_vtab *vtab, const char *newname) +{ + return SQLITE_OK; +} + +#endif + +/** + * SQLite module descriptor. + */ + +static const sqlite3_module zip_vtab_mod = { + 1, /* iVersion */ + zip_vtab_create, /* xCreate */ + zip_vtab_connect, /* xConnect */ + zip_vtab_bestindex, /* xBestIndex */ + zip_vtab_disconnect, /* xDisconnect */ + zip_vtab_destroy, /* xDestroy */ + zip_vtab_open, /* xOpen */ + zip_vtab_close, /* xClose */ + zip_vtab_filter, /* xFilter */ + zip_vtab_next, /* xNext */ + zip_vtab_eof, /* xEof */ + zip_vtab_column, /* xColumn */ + zip_vtab_rowid, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + zip_vtab_findfunc, /* xFindFunction */ +#if (SQLITE_VERSION_NUMBER > 3004000) + zip_vtab_rename, /* xRename */ +#endif +}; + +/** + * Compute CRC32 given blob + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +zip_crc32_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + int crc, length; + unsigned char *data; + + if (argc != 1) { + sqlite3_result_error(ctx, "need one argument", -1); + } + data = (unsigned char *) sqlite3_value_blob(argv[0]); + length = sqlite3_value_bytes(argv[0]); + crc = crc32(0, 0, 0); + if (data && (length > 0)) { + crc = crc32(crc, data, length); + } + sqlite3_result_int(ctx, crc); +} + +/** + * Inflate data given blob + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +zip_inflate_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + int err, length, dlength, avail; + unsigned char *data, *dest, *newdest; + z_stream stream; + + if (argc != 1) { + sqlite3_result_error(ctx, "need one argument", -1); + return; + } + data = (unsigned char *) sqlite3_value_blob(argv[0]); + length = sqlite3_value_bytes(argv[0]); + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.next_in = data; + stream.avail_in = length; + avail = length; + stream.next_out = dest = sqlite3_malloc(avail); + stream.avail_out = avail; + stream.opaque = 0; + if (!dest) { + goto oom; + } + if (inflateInit2(&stream, -15) != Z_OK) { + goto nomem; + } + dlength = 0; + while (1) { + err = inflate(&stream, Z_SYNC_FLUSH); + if ((err == Z_STREAM_END) || + ((err == Z_OK) && (stream.avail_in == 0))) { + dlength += length - stream.avail_out; + newdest = sqlite3_realloc(dest, dlength); + inflateEnd(&stream); + if (!newdest) { +nomem: + if (dest) { + sqlite3_free(dest); + } +oom: + sqlite3_result_error_nomem(ctx); + return; + } + sqlite3_result_blob(ctx, newdest, dlength, sqlite3_free); + return; + } + if ((err == Z_BUF_ERROR) || (err == Z_OK)) { + newdest = sqlite3_realloc(dest, avail + length); + dlength += length - stream.avail_out; + if (!newdest) { + inflateEnd(&stream); + goto nomem; + } + avail += length; + stream.next_out = newdest + (stream.next_out - dest); + dest = newdest; + stream.avail_out += length; + } else { + inflateEnd(&stream); + sqlite3_free(dest); + sqlite3_result_error(ctx, "inflate error", -1); + return; + } + } +} + +/** + * Deflate data given blob and optional compression level + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +zip_deflate_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + int err, level = 9; + unsigned long avail, length; + unsigned char *data, *dest = 0; + z_stream stream; + + if ((argc < 1) || (argc > 2)) { + sqlite3_result_error(ctx, "need one or two arguments", -1); + return; + } + if (argc > 1) { + level = sqlite3_value_int(argv[1]); + } + data = (unsigned char *) sqlite3_value_blob(argv[0]); + length = sqlite3_value_bytes(argv[0]); + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.next_in = data; + stream.avail_in = length; + stream.next_out = 0; + stream.avail_out = 0; + stream.opaque = 0; + if (deflateInit2(&stream, level, Z_DEFLATED, -15, 8, + Z_DEFAULT_STRATEGY) != Z_OK) { + goto deflerr; + } + avail = deflateBound(&stream, length); + if (avail == 0) { + sqlite3_result_null(ctx); + return; + } + stream.next_out = dest = sqlite3_malloc(avail); + stream.avail_out = avail; + if (!dest) { + sqlite3_result_error_nomem(ctx); + return; + } + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); +deflerr: + if (dest) { + sqlite3_free(dest); + } + sqlite3_result_error(ctx, "deflate error", -1); + return; + } + length = stream.total_out; + err = deflateEnd(&stream); + if (err != Z_OK) { + goto deflerr; + } + sqlite3_result_blob(ctx, dest, length, sqlite3_free); +} + +/** + * Compress data given blob and optional compression level + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + */ + +static void +zip_compress_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + int err, level = 9; + unsigned long length, dlength; + unsigned char *data, *dest; + + if ((argc < 1) || (argc > 2)) { + sqlite3_result_error(ctx, "need one or two arguments", -1); + return; + } + if (argc > 1) { + level = sqlite3_value_int(argv[1]); + } + data = (unsigned char *) sqlite3_value_blob(argv[0]); + length = sqlite3_value_bytes(argv[0]); + dlength = compressBound(length); + dest = sqlite3_malloc(dlength); + if (!dest) { + sqlite3_result_error_nomem(ctx); + return; + } + err = compress2(dest, &dlength, data, length, level); + if (err == Z_OK) { + sqlite3_result_blob(ctx, dest, dlength, sqlite3_free); + return; + } + if (err == Z_MEM_ERROR) { + sqlite3_result_error(ctx, "memory error", -1); + } else if (err == Z_BUF_ERROR) { + sqlite3_result_error(ctx, "buffer error", -1); + } else { + sqlite3_result_error(ctx, "compress error", -1); + } + sqlite3_free(dest); +} + +#ifdef SQLITE_OPEN_URI + +/** + * Create mem_blk from given data buffer and length. + * @param data data buffer + * @param length length of buffer + * @result pointer to mem_blk or NULL + */ + +static mem_blk * +mem_createmb(const unsigned char *data, unsigned long length) +{ + mem_blk *mb; +#if defined(_WIN32) || defined(_WIN64) + HANDLE mh; +#else + long psize; +#endif + unsigned long size; + +#if defined(_WIN32) || defined(_WIN64) + size = sizeof (mem_blk) + length; + mh = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, + 0, size, 0); + if (mh == INVALID_HANDLE_VALUE) { + return 0; + } + mb = (mem_blk *) MapViewOfFile(mh, FILE_MAP_ALL_ACCESS, 0, 0, length); + if (!mb) { + return 0; + } +#else + psize = sysconf(_SC_PAGESIZE); +#ifdef linux + mb = (mem_blk *) sqlite3_malloc(sizeof (mem_blk)); + if (!mb) { + return 0; + } + size = length + 1; + mb->data = (unsigned char *) mmap(0, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (mb->data == MAP_FAILED) { + sqlite3_free(mb); + return 0; + } +#else + size = sizeof (mem_blk) + psize + length + 1; + mb = (mem_blk *) mmap(0, size, PROT_READ | PROT_WRITE, +#if defined(MAP_ANONYMOUS) + MAP_ANONYMOUS | +#elif defined(MAP_ANON) + MAP_ANON | +#endif + MAP_PRIVATE, -1, 0); + if (mb == MAP_FAILED) { + return 0; + } +#endif +#endif + if (mb) { + memcpy(mb->magic, MEM_MAGIC, 4); + mb->opened = 1; + mb->size = size; + mb->length = length; +#if defined(_WIN32) || defined(_WIN64) + mb->mh = mh; + mb->data = (unsigned char *) (mb + 1); + memcpy(mb->data, data, length); +#else + mb->psize = psize; +#ifdef linux + mb->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + sqlite3_mutex_enter(mb->mutex); + mb->lcnt = 0; + memcpy(mb->data, data, length); +#else + if (psize >= sizeof (mem_blk)) { + mb->data = (unsigned char *) mb + psize; + memcpy(mb->data, data, length); +#ifndef linux + mprotect(mb->data, length, PROT_READ); +#endif + } else { + mb->data = (unsigned char *) (mb + 1); + memcpy(mb->data, data, length); + } +#endif +#endif + } + return mb; +} + +/** + * Destroy given mem_blk. + * @param mb mem_blk pointer + */ + +static void +mem_destroymb(mem_blk *mb) +{ +#if defined(_WIN32) || defined(_WIN64) + HANDLE mh; +#endif + + if (mb) { + memset(mb->magic, 0, 4); +#if defined(_WIN32) || defined(_WIN64) + mh = mb->mh; + UnmapViewOfFile(mb); + CloseHandle(mh); +#else +#ifdef linux + munmap(mb->data, mb->size); + sqlite3_mutex_leave(mb->mutex); + sqlite3_mutex_free(mb->mutex); + sqlite3_free(mb); +#else + munmap(mb, mb->size); +#endif +#endif + } +} + +/** + * Close mem_file and release associated mem_blk if open count drops to zero. + * @param file SQLite3 file pointer + * @result SQLite error code + */ + +static int +mem_close(sqlite3_file *file) +{ + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + + if (mb) { +#ifdef linux + sqlite3_mutex_enter(mb->mutex); + if (mf->lock > 0) { + mb->lcnt = 0; + } +#endif + mb->opened--; + if (mb->opened <= 0) { + mem_destroymb(mb); + } +#ifdef linux + else { + sqlite3_mutex_leave(mb->mutex); + } +#endif + mf->mb = 0; + } + return SQLITE_OK; +} + +/** + * Read data from mem_file. + * @param file SQLite3 file pointer + * @param buf where to read data to + * @param len length to be read + * @param offs file offset + * @result SQLite error code + */ + +static int +mem_read(sqlite3_file *file, void *buf, int len, sqlite_int64 offs) +{ + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + int rc = SQLITE_IOERR_READ; + +#ifdef linux + if (mb) { + sqlite3_mutex_enter(mb->mutex); + } +#endif + if (mb && (offs <= mb->length)) { + rc = SQLITE_OK; + if (offs + len > mb->length) { + rc = SQLITE_IOERR_SHORT_READ; + len = mb->length - offs; + } + memcpy(buf, mb->data + offs, len); + } +#ifdef linux + if (mb) { + sqlite3_mutex_leave(mb->mutex); + } +#endif + return rc; +} + +/** + * Truncate mem_file. + * @param file SQLite3 file pointer + * @param offs file size + * @result SQLite error code + */ + +static int +#ifdef linux +mem_truncate_unlocked(sqlite3_file *file, sqlite_int64 offs) +#else +mem_truncate(sqlite3_file *file, sqlite_int64 offs) +#endif +{ +#ifdef linux + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + unsigned char *p; + long psize = mb->psize; + unsigned long length = offs; + unsigned long size; + + size = length + 1; + if ((psize > 0) && (size / psize == mb->size / psize)) { + p = mb->data; + } else { + p = mremap(mb->data, mb->size, size, MREMAP_MAYMOVE); + } + if (p == MAP_FAILED) { + return SQLITE_IOERR_TRUNCATE; + } + mb->size = size; + mb->length = length; + mb->data = p; + return SQLITE_OK; +#else + return SQLITE_IOERR_TRUNCATE; +#endif +} + +#ifdef linux +static int +mem_truncate(sqlite3_file *file, sqlite_int64 offs) +{ + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + int rc = SQLITE_IOERR_TRUNCATE; + + if (mb) { + sqlite3_mutex_enter(mb->mutex); + rc = mem_truncate_unlocked(file, offs); + sqlite3_mutex_leave(mb->mutex); + } + return rc; +} +#endif + +/** + * Write data to mem_file. + * @param file SQLite3 file pointer + * @param buf what to write + * @param len length to be written + * @param offs file offset + * @result SQLite error code + */ + +static int +mem_write(sqlite3_file *file, const void *buf, int len, sqlite_int64 offs) +{ +#ifdef linux + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + + sqlite3_mutex_enter(mb->mutex); + if (offs + len > mb->length) { + if (mem_truncate_unlocked(file, offs + len) != SQLITE_OK) { + sqlite3_mutex_leave(mb->mutex); + return SQLITE_IOERR_WRITE; + } + } + memcpy(mb->data + offs, buf, len); + sqlite3_mutex_leave(mb->mutex); + return SQLITE_OK; +#else + return SQLITE_IOERR_WRITE; +#endif +} + +/** + * Sync mem_file. + * @param file SQLite3 file pointer + * @param flags sync flags + * @result SQLite error code + */ + +static int +mem_sync(sqlite3_file *file, int flags) +{ +#ifdef linux + return SQLITE_OK; +#else + return SQLITE_IOERR_FSYNC; +#endif +} + +/** + * Report file size of mem_file. + * @param file SQLite3 file pointer + * @param size value return + * @result SQLite error code + */ + +static int +mem_filesize(sqlite3_file *file, sqlite_int64 *size) +{ + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + + if (mb) { +#ifdef linux + sqlite3_mutex_enter(mb->mutex); +#endif + *size = mb->length; +#ifdef linux + sqlite3_mutex_leave(mb->mutex); +#endif + return SQLITE_OK; + } + return SQLITE_IOERR_FSTAT; +} + +/** + * Lock mem_file. + * @param file SQLite3 file pointer + * @param lck new lock status + * @result SQLite error code, always SQLITE_OK + */ + +static int +mem_lock(sqlite3_file *file, int lck) +{ +#ifdef linux + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + int rc = SQLITE_IOERR_LOCK; + + if (mb) { + sqlite3_mutex_enter(mb->mutex); + if (lck > 0) { + rc = SQLITE_BUSY; + if ((mf->lock == 0) && (mb->lcnt == 0)) { + mb->lcnt = 1; + mf->lock = lck; + rc = SQLITE_OK; + } else if ((mf->lock > 0) && (mb->lcnt == 1)) { + mf->lock = lck; + rc = SQLITE_OK; + } + } + sqlite3_mutex_leave(mb->mutex); + } + return rc; +#else + return SQLITE_OK; +#endif +} + +/** + * Unlock mem_file. + * @param file SQLite3 file pointer + * @param lck new lock status + * @result SQLite error code, always SQLITE_OK + */ + +static int +mem_unlock(sqlite3_file *file, int lck) +{ +#ifdef linux + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + int rc = SQLITE_IOERR_UNLOCK; + + if (mb) { + sqlite3_mutex_enter(mb->mutex); + if (mf->lock == lck) { + rc = SQLITE_OK; + } else if (lck == 0) { + if (mf->lock) { + mb->lcnt = 0; + mf->lock = 0; + } + rc = SQLITE_OK; + } else if ((lck < mf->lock) && (mb->lcnt != 0)) { + mf->lock = lck; + rc = SQLITE_OK; + } + sqlite3_mutex_leave(mb->mutex); + } + return rc; +#else + return SQLITE_OK; +#endif +} + +/** + * Check lock state of mem_file. + * @param file SQLite3 file pointer + * @param out current lock status + * @result SQLite error code, always SQLITE_OK + */ + +static int +mem_checkreservedlock(sqlite3_file *file, int *out) +{ +#ifdef linux + mem_file *mf = (mem_file *) file; + mem_blk *mb = mf->mb; + int rc = SQLITE_IOERR_CHECKRESERVEDLOCK; + + if (mb) { + sqlite3_mutex_enter(mb->mutex); + *out = mf->lock >= 2; + sqlite3_mutex_leave(mb->mutex); + rc = SQLITE_OK; + } else { + *out = 0; + } + return rc; +#else + *out = 0; + return SQLITE_OK; +#endif +} + +/** + * File control operation on mem_file. + * @param file SQLite3 file pointer + * @param op operation code + * @param arg argument for operation + * @result SQLite error code, always SQLITE_OK + */ + +static int +mem_filecontrol(sqlite3_file *file, int op, void *arg) +{ +#ifdef SQLITE_FCNTL_PRAGMA + if (op == SQLITE_FCNTL_PRAGMA) { + return SQLITE_NOTFOUND; + } +#endif + return SQLITE_OK; +} + +/** + * Report sector size of mem_file. + * @param file SQLite3 file pointer + * @result always 4096 + */ + +static int +mem_sectorsize(sqlite3_file *file) +{ + return 4096; +} + +/** + * Device characteristics of mem_file. + * @param file SQLite3 file pointer + * @result always 0 + */ + +static int +mem_devicecharacteristics(sqlite3_file *file) +{ + return 0; +} + +/** + * I/O method structure of mem_file. + */ + +static sqlite3_io_methods mem_methods = { + 1, /* iVersion */ + mem_close, /* xClose */ + mem_read, /* xRead */ + mem_write, /* xWrite */ + mem_truncate, /* xTruncate */ + mem_sync, /* xSync */ + mem_filesize, /* xFileSize */ + mem_lock, /* xLock */ + mem_unlock, /* xUnlock */ + mem_checkreservedlock, /* xCheckReservedLock */ + mem_filecontrol, /* xFileControl */ + mem_sectorsize, /* xSectorSize */ + mem_devicecharacteristics /* xDeviceCharacteristics */ +}; + +/** + * Open mem_file given file name in mem_vfs. + * @param vfs SQLite VFS + * @param name file name + * @param file SQLite3 file pointer to be filled + * @param flags open flags + * @param outflags value return of open flags + * @result SQLite error code + */ + +static int +mem_open(sqlite3_vfs *vfs, const char *name, sqlite3_file *file, + int flags, int *outflags) +{ + mem_file *mf = (mem_file *) file; + mem_blk *mb = 0; +#ifdef _WIN64 + unsigned long long t = 0; +#else + unsigned long t = 0; +#endif +#if !defined(_WIN32) && !defined(_WIN64) + mem_blk mb0; + int pfd[2]; + int n; +#endif + + if (!name) { + return SQLITE_IOERR; + } + if (flags & (SQLITE_OPEN_MAIN_JOURNAL | + SQLITE_OPEN_WAL | +#ifndef linux + SQLITE_OPEN_READWRITE | +#endif + SQLITE_OPEN_CREATE)) { + return SQLITE_CANTOPEN; + } +#ifdef _WIN64 + sscanf(name + 1, "%I64x", &t); +#else + t = strtoul(name + 1, 0, 16); +#endif + mb = (mem_blk *) t; + if (!mb) { + return SQLITE_CANTOPEN; + } +#if !defined(_WIN32) && !defined(_WIN64) + if (pipe(pfd) < 0) { + return SQLITE_CANTOPEN; + } + n = (write(pfd[1], (char *) mb, sizeof (mem_blk)) < 0) ? errno : 0; + if (n == EFAULT) { +cantopen: + close(pfd[0]); + close(pfd[1]); + return SQLITE_CANTOPEN; + } + n = read(pfd[0], (char *) &mb0, sizeof (mem_blk)); + if (n != sizeof (mem_blk)) { + goto cantopen; + } + if (memcmp(mb0.magic, MEM_MAGIC, 4) == 0) { +#ifdef linux + n = (write(pfd[1], (char *) mb0.data, 1) < 0) ? errno : 0; + if (n == EFAULT) { + goto cantopen; + } +#endif + if (mb0.length > 0) { + n = (write(pfd[1], (char *) mb0.data + mb0.length - 1, 1) < 0) + ? errno : 0; + if (n == EFAULT) { + goto cantopen; + } + } + close(pfd[0]); + close(pfd[1]); +#ifdef linux + sqlite3_mutex_enter(mb->mutex); +#endif + mb->opened++; +#ifdef linux + sqlite3_mutex_leave(mb->mutex); +#endif + } else { + goto cantopen; + } +#else + if (memcmp(mb->magic, MEM_MAGIC, 4) == 0) { + mb->opened++; + } else { + return SQLITE_CANTOPEN; + } +#endif + memset(mf, 0, sizeof (mem_file)); + mf->mb = mb; + mf->base.pMethods = &mem_methods; + if (outflags) { + *outflags = flags; + } + return SQLITE_OK; +} + +/** + * Delete mem_vfs file given name. + * @param vfs SQLite VFS + * @param name file name + * @param sync perform sync before deletion + * @result SQLite error code, always SQLITE_IOERR_DELETE + */ + +static int +mem_delete(sqlite3_vfs *vfs, const char *name, int sync) +{ + return SQLITE_IOERR_DELETE; +} + +/** + * Test mem_vfs file access given name and flags + * @param vfs SQLite VFS + * @param name file name + * @param flags access to be tested + * @param outflags value return of tested access modes + * @result SQLite error code + */ + +static int +mem_access(sqlite3_vfs *vfs, const char *name, int flags, int *outflags) +{ + char *endp = 0; + unsigned long t; + + t = strtol(name + 1, &endp, 16); + if ((t == 0) || +#ifndef linux + (flags == SQLITE_ACCESS_READWRITE) || +#endif + !endp || endp[0]) { + *outflags = 0; + } else { + *outflags = 1; + } + return SQLITE_OK; +} + +/** + * Return full pathname on mem_vfs given name. + * @param vfs SQLite VFS + * @param name file name + * @param len length of output buffer + * @param out output buffer + * @result SQLite error code, always SQLITE_OK + */ + +static int +mem_fullpathname(sqlite3_vfs *vfs, const char *name, int len, char *out) +{ + strncpy(out, name, len); + out[len - 1] = '\0'; + return SQLITE_OK; +} + +/** + * Open shared library on mem_vfs given name. + * @param vfs SQLite VFS + * @param name file name + * @result handle, always 0 + */ + +static void * +mem_dlopen(sqlite3_vfs *vfs, const char *name) +{ + return 0; +} + +/** + * Report last error of shared library operation on mem_vfs. + * @param vfs SQLite VFS + * @param len length of output buffer + * @param out output buffer + */ + +static void +mem_dlerror(sqlite3_vfs *vfs, int len, char *out) +{ + static const char *errtxt = "Loadable extensions are not supported"; + + strncpy(out, errtxt, strlen(errtxt)); + out[len - 1] = '\0'; +} + +/** + * Lookup symbol in shared library on mem_vfs given name. + * @param vfs SQLite VFS + * @param handle shared library handle + * @param sym symbol name + * @result symbol address, always 0 + */ + +static void +(*mem_dlsym(sqlite3_vfs *vfs, void *handle, const char *sym))(void) +{ + return 0; +} + +/** + * Close shared library on mem_vfs given handle. + * @param vfs SQLite VFS + * @param handle shared library handle + */ + +static void +mem_dlclose(sqlite3_vfs *vfs, void *handle) +{ +} + +/** + * Return buffer filled with random bytes + * @param vfs SQLite VFS + * @param len length of output buffer + * @param out output buffer + * @result SQLite error code + */ + +static int +mem_randomness(sqlite3_vfs *vfs, int len, char *out) +{ + sqlite3_vfs *ovfs = (sqlite3_vfs *) vfs->pAppData; + + return ovfs->xRandomness(ovfs, len, out); +} + +/** + * Sleep for given number of microseconds. + * @param vfs SQLite VFS + * @param micro microseconds to sleep + * @result SQLite error code + */ + +static int +mem_sleep(sqlite3_vfs *vfs, int micro) +{ + sqlite3_vfs *ovfs = (sqlite3_vfs *) vfs->pAppData; + + return ovfs->xSleep(ovfs, micro); +} + +/** + * Return current time. + * @param vfs SQLite VFS + * @param out output buffer + * @result SQLite error code + */ + +static int +mem_currenttime(sqlite3_vfs *vfs, double *out) +{ + sqlite3_vfs *ovfs = (sqlite3_vfs *) vfs->pAppData; + + return ovfs->xCurrentTime(ovfs, out); +} + +/** + * VFS structure of mem_vfs + */ + +static sqlite3_vfs mem_vfs = { + 1, /* iVersion */ + sizeof (mem_file), /* szOsFile */ + 256, /* mxPathname */ + 0, /* pNext */ + mem_vfs_name, /* zName */ + 0, /* pAppData */ + mem_open, /* xOpen */ + mem_delete, /* xDelete */ + mem_access, /* xAccess */ + mem_fullpathname, /* xFullPathname */ + mem_dlopen, /* xDlOpen */ + mem_dlerror, /* xDlError */ + mem_dlsym, /* xDlSym */ + mem_dlclose, /* xDlClose */ + mem_randomness, /* xDlError */ + mem_sleep, /* xDlSym */ + mem_currenttime /* xDlClose */ +}; + +/** + * Attach (read-only) embedded SQLite database given blob. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Arguments: + * + * BLOB - the BLOB containing the embedded SQLite database<br> + * dbname - the name of the attached database<br> + * + * Function result: + * + * NULL - attached r/o database<br> + * URI string - attached database<br> + * all else - error occurred<br> + * + * Example: + * + * CREATE VIRTUAL TABLE Z USING ZIPFILE('zipfile.zip');<br> + * SELECT blob_attach(data, 'ZDB') FROM Z WHERE PATH = 'embedded.db';<br> + * DROP VIRTUAL TABLE Z;<br> + */ + +static void +blob_attach_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + unsigned long length; + const unsigned char *data; + mem_blk *mb = 0; + char *sql = 0; + int sqllen = 0; +#ifdef linux + int isrw = 0; +#endif + + if (argc != 2) { + sqlite3_result_error(ctx, "need two arguments", -1); + return; + } + data = (const unsigned char *) sqlite3_value_blob(argv[0]); + length = sqlite3_value_bytes(argv[0]); + if (!data || !length) { + sqlite3_result_error(ctx, "empty blob", -1); + return; + } + mb = mem_createmb(data, length); + if (!mb) { + sqlite3_result_error(ctx, "cannot map blob", -1); + return; + } + sql = sqlite3_mprintf("ATTACH " +#ifdef _WIN64 + "'file:/%llX" +#else + "'file:/%lX" +#endif + "?vfs=%s&" +#ifdef linux + "mode=rw&" +#else + "mode=ro&" +#endif + "cache=private' AS %Q", +#ifdef _WIN64 + (unsigned long long) mb, +#else + (unsigned long) mb, +#endif + mem_vfs_name, + (char *) sqlite3_value_text(argv[1])); + if (!sql) { + sqlite3_result_error(ctx, "cannot map blob", -1); + mem_destroymb(mb); + return; + } +#ifdef linux + sqlite3_mutex_leave(mb->mutex); +#endif + if (sqlite3_exec(sqlite3_context_db_handle(ctx), sql, 0, 0, 0) + != SQLITE_OK) { + sqlite3_free(sql); + sqlite3_result_error(ctx, "cannot attach blob", -1); +#ifdef linux + sqlite3_mutex_enter(mb->mutex); +#endif + mem_destroymb(mb); + return; + } + sqllen = strlen(sql); + sqlite3_snprintf(sqllen, sql, "PRAGMA %Q.synchronous = OFF", + (char *) sqlite3_value_text(argv[1])); + sqlite3_exec(sqlite3_context_db_handle(ctx), sql, 0, 0, 0); +#ifdef linux + sqlite3_snprintf(sqllen, sql, "PRAGMA %Q.journal_mode = OFF", + (char *) sqlite3_value_text(argv[1])); + if (sqlite3_exec(sqlite3_context_db_handle(ctx), sql, 0, 0, 0) + == SQLITE_OK) { + isrw = 1; + } +#endif +#ifdef linux + sqlite3_mutex_enter(mb->mutex); +#endif + if (--mb->opened < 1) { + sqlite3_snprintf(sqllen, sql, "DETACH %Q", + (char *) sqlite3_value_text(argv[1])); + sqlite3_exec(sqlite3_context_db_handle(ctx), sql, 0, 0, 0); + sqlite3_free(sql); + sqlite3_result_error(ctx, "cannot attach blob", -1); + mem_destroymb(mb); + return; + } +#ifdef linux + sqlite3_mutex_leave(mb->mutex); + if (isrw) { + sqlite3_snprintf(sqllen, sql, + "file:/%lX?vfs=%s&mode=rw&cache=private", + (unsigned long) mb, mem_vfs_name); + sqlite3_result_text(ctx, sql, -1, sqlite3_free); + return; + } +#endif + sqlite3_free(sql); + sqlite3_result_null(ctx); +} + +/** + * Dump memory mapped (writable) database to blob. + * @param ctx SQLite function context + * @param argc number of arguments + * @param argv argument vector + * + * Arguments: + * + * ADDR - the memory mapped database<br> + * + * Function result: + * + * blob - success<br> + * all else - error occurred<br> + * + */ + +static void +blob_dump_func(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + char *uri, vfs[64]; +#ifdef _WIN64 + unsigned long long addr = 0; +#else + unsigned long addr = 0; +#endif +#ifdef linux + int pfd[2], n; + mem_blk *mb; +#endif + + if (argc != 1) { + sqlite3_result_error(ctx, "need one argument", -1); + return; + } + uri = (char *) sqlite3_value_text(argv[0]); + vfs[0] = '\0'; + if (!uri || (sscanf(uri, +#ifdef _WIN64 + "file:/%I64X?vfs=%63[^&]", +#else + "file:/%lX?vfs=%63[^&]", +#endif + &addr, vfs) != 2)) { +inval: + sqlite3_result_error(ctx, "invalid object", -1); + return; + } + vfs[63] = '\0'; + if ((strcmp(mem_vfs_name, vfs) != 0) || (addr == 0)) { + goto inval; + } +#ifdef linux + if (pipe(pfd) < 0) { + goto inval; + } + n = (write(pfd[1], (char *) addr, 1) < 0) ? errno : 0; + close(pfd[0]); + close(pfd[1]); + if (n == EFAULT) { + goto inval; + } + mb = (mem_blk *) addr; + if (memcmp(mb->magic, MEM_MAGIC, 4) != 0) { + goto inval; + } + sqlite3_mutex_enter(mb->mutex); + sqlite3_result_blob(ctx, mb->data, mb->length, SQLITE_STATIC); + sqlite3_mutex_leave(mb->mutex); +#else + sqlite3_result_error(ctx, "unsupported function", -1); +#endif +} + +#endif /* SQLITE_OPEN_URI */ + +/** + * Module initializer creating SQLite module and functions. + * @param db database pointer + * @result SQLite error code + */ + +#ifndef STANDALONE +static +#endif +int +zip_vtab_init(sqlite3 *db) +{ + sqlite3_create_function(db, "crc32", 1, SQLITE_UTF8, + (void *) db, zip_crc32_func, 0, 0); + sqlite3_create_function(db, "inflate", 1, SQLITE_UTF8, + (void *) db, zip_inflate_func, 0, 0); + sqlite3_create_function(db, "deflate", 1, SQLITE_UTF8, + (void *) db, zip_deflate_func, 0, 0); + sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, + (void *) db, zip_inflate_func, 0, 0); + sqlite3_create_function(db, "compress", -1, SQLITE_UTF8, + (void *) db, zip_compress_func, 0, 0); +#ifdef SQLITE_OPEN_URI + if (!mem_vfs.pAppData) { + sqlite3_vfs *parent = sqlite3_vfs_find(0); + + if (parent) { + sqlite3_snprintf(sizeof (mem_vfs_name), mem_vfs_name, +#ifdef _WIN64 + "mem_vfs_%llX", (unsigned long long) &mem_vfs +#else + "mem_vfs_%lX", (unsigned long) &mem_vfs +#endif + ); + if (sqlite3_vfs_register(&mem_vfs, 0) == SQLITE_OK) { + mem_vfs.pAppData = (void *) parent; + } + } + } + if (mem_vfs.pAppData) { + sqlite3_create_function(db, "blob_attach", 2, SQLITE_UTF8, + (void *) db, blob_attach_func, 0, 0); + sqlite3_create_function(db, "blob_dump", 1, SQLITE_UTF8, + (void *) db, blob_dump_func, 0, 0); + } +#endif + return sqlite3_create_module(db, "zipfile", &zip_vtab_mod, 0); +} + +#ifndef STANDALONE + +/** + * Initializer for SQLite extension load mechanism. + * @param db SQLite database pointer + * @param errmsg pointer receiving error message + * @param api SQLite API routines + * @result SQLite error code + */ + +int +sqlite3_extension_init(sqlite3 *db, char **errmsg, + const sqlite3_api_routines *api) +{ + SQLITE_EXTENSION_INIT2(api); + return zip_vtab_init(db); +} + +#endif diff --git a/lang/sql/odbc/zipfile.rc b/lang/sql/odbc/zipfile.rc new file mode 100644 index 00000000..6627232a --- /dev/null +++ b/lang/sql/odbc/zipfile.rc @@ -0,0 +1,48 @@ +#include <winresrc.h> +#include <winver.h> +#include "resource3.h" +#include "sqlite3.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_C,0,0 + PRODUCTVERSION VERSION_C,0,0 + FILEFLAGSMASK (3) + FILEFLAGS (0) + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE (0) +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "Christian Werner Software & Consulting\0" + VALUE "FileDescription", "SQLite3 Extension ZIP File\0" + VALUE "FileVersion", VERSION "\0" + VALUE "InternalName", "ZIPFILE\0" + VALUE "LegalCopyright", "Public Domain\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "SQLITE3_MOD_ZIPFILE.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "ODBC Driver for SQLite3 " SQLITE_VERSION "\0" + VALUE "ProductVersion", VERSION "\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +ico1 ICON "sqliteodbc.ico" |
