From 6622cce37290b0d26ac876405782ff815fb6f944 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Thu, 20 May 2004 11:00:52 +0000 Subject: [PATCH] Add some more elements of the new API. (CVS 1416) FossilOrigin-Name: 2821767b947ae1a70e98dd7f47d69e424c37947f --- manifest | 31 +++---- manifest.uuid | 2 +- src/main.c | 199 +++++++++++++++++++++++++++++++++--------- src/os.h | 8 ++ src/sqlite.h.in | 79 +++++++++++++---- src/sqliteInt.h | 14 ++- src/test1.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++- src/utf.c | 79 ++++++++++++++--- src/util.c | 44 +++++++++- src/vdbeaux.c | 24 +++++- test/bind.test | 29 ++++++- test/capi3.test | 110 ++++++++++++++++++++++++ 12 files changed, 747 insertions(+), 95 deletions(-) create mode 100644 test/capi3.test diff --git a/manifest b/manifest index d2fff1a8b6..2174a9ee6c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\smisc3.test\stest\sfile.\s(CVS\s1415) -D 2004-05-20T03:30:11 +C Add\ssome\smore\selements\sof\sthe\snew\sAPI.\s(CVS\s1416) +D 2004-05-20T11:00:52 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -36,10 +36,10 @@ F src/func.c cfbb7096efb58e2857e3b312a8958a12774b625a F src/hash.c 440c2f8cb373ee1b4e13a0988489c7cd95d55b6f F src/hash.h 762d95f1e567664d1eafc1687de755626be962fb F src/insert.c 04865f0a8a5cbc81eab7ca7406498d5356ef0763 -F src/main.c 2bfbddaea0a1db592967f4b0844473a24adce2fe +F src/main.c 1bbb26e37c167443ee4c6c27b9150744be01ba81 F src/md5.c 8e39fdae6d8776b87558e91dcc94740c9b635a9c F src/os.c ddcda92f7fd71b4513c57c1ec797917f206d504e -F src/os.h fbb2f6595fc34fa351830d88fe1c6b85118f0383 +F src/os.h 6e446a17cbeb6c2ce470683a0bb8d9c63abe8607 F src/pager.c 6ff6b906427d4824099140776cb8768f922f3dc5 F src/pager.h 78a00ac280899bcba1a89dc51585dcae6b7b3253 F src/parse.y d2e226650738931c047c2562326ed05882af2330 @@ -48,11 +48,11 @@ F src/printf.c ef750e8e2398ca7e8b58be991075f08c6a7f0e53 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c 2510f0f16bf28108d89ba6e5680a9f090adc31b7 F src/shell.c 0c4662e13bfbfd3d13b066c5859cc97ad2f95d21 -F src/sqlite.h.in f9c9ffd435312810a59a4af055021b1a22d78c28 -F src/sqliteInt.h 6b43ef88542c242b39ed41a84055e4504571a9f0 +F src/sqlite.h.in 8c000826a517ac7302dc2e1c483e71cd06eaf0de +F src/sqliteInt.h 50ec7fb9635403ee71698d9fe0867564b915c52a F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 F src/tclsqlite.c fbf0fac73624ae246551a6c671f1de0235b5faa1 -F src/test1.c 9b068ec5488d1845fc890d25fc84e8577a0dff98 +F src/test1.c cc6061579f0d9f1e952ad746394c34afd66a64f2 F src/test2.c 6195a1ca2c8d0d2d93644e86da3289b403486872 F src/test3.c 5e4a6d596f982f6f47a5f9f75ede9b4a3b739968 F src/test4.c b3fab9aea7a8940a8a7386ce1c7e2157b09bd296 @@ -60,13 +60,13 @@ F src/test5.c c92dca7028b19b9c8319d55e0a5037fc183640a6 F src/tokenize.c e7536dd31205d5afb76c1bdc832dea009c7a3847 F src/trigger.c 11afe9abfba13a2ba142944c797c952e162d117f F src/update.c 1f6687f8d1085f896a24c0fa13d802223ed55539 -F src/utf.c 72a9843000985d11100e711e0ef06ff4b8946057 -F src/util.c 35d20bd8d467861747262d12b87045d937781d93 +F src/utf.c c27c4f1120f7aaef00cd6942b3d9e3f4ca4fe0e4 +F src/util.c 5cbeb452da09cfc7248de9948c15b14d840723f7 F src/vacuum.c c134702e023db8778e6be59ac0ea7b02315b5476 F src/vdbe.c fe412966e48948b680a5bc25796e6e9727174b4b F src/vdbe.h 314e9c07db73a42a6ba91ab7539e27652fc88870 F src/vdbeInt.h 97b95c622ea467d39879ae97d07732ebb4891b76 -F src/vdbeaux.c 0039a786c6d1c1eeff36f50ad2c528de70e11b26 +F src/vdbeaux.c c48157e910b16010812f67daf981141d3b913e64 F src/where.c 626b2cbc4290d8be6c04ad7c8395f46d4e21d0d8 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/attach.test cb9b884344e6cfa5e165965d5b1adea679a24c83 @@ -74,13 +74,14 @@ F test/attach2.test 7c388dee63a4c1997695c3d41957f32ce784ac56 F test/auth.test 5c4d95cdaf539c0c236e20ce1f71a93e7dde9185 F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 -F test/bind.test b0676476d1ccf210867ad972fa45d9ca0b9906c3 +F test/bind.test 5d6ac8ab0dbec03ce79284abefa9230726cb9dad F test/btree.test 08e4093c78d2bc1d54e27266f8d17fed14751125 F test/btree2.test aa4a6d05b1ea90b1acaf83ba89039dd302a88635 F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4 F test/btree5.test 8e5ff32c02e685d36516c6499add9375fe1377f2 F test/btree6.test b7524d7165faff496a767dfa2c78a1ae4d8ba09a F test/capi2.test ec96e0e235d87b53cbaef3d8e3e0f8ccf32c71ca +F test/capi3.test acc3919c0f37e85ac3561fc33d6abf7183e1aee1 F test/conflict.test 0911bb2f079046914a6e9c3341b36658c4e2103e F test/copy.test f07ea8d60878da7a67416ab62f78e9706b9d3c45 F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 @@ -193,7 +194,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P a7f02db73881eb2d899daa93646962960dac5375 -R c220a1c9147b9aa82b7860572b068793 -U drh -Z 766a086b9d46979d214b9aad7633d80b +P 478836f44825d8154c0106e46e9a2b2daaa4cf33 +R 805cabda484d77e19688e2aa36d12345 +U danielk1977 +Z 602944ecee18511772e5b9fe5606a0b8 diff --git a/manifest.uuid b/manifest.uuid index a32f3c5447..65f6a51359 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -478836f44825d8154c0106e46e9a2b2daaa4cf33 \ No newline at end of file +2821767b947ae1a70e98dd7f47d69e424c37947f \ No newline at end of file diff --git a/src/main.c b/src/main.c index 09b6bcb211..d0e7297ce4 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.176 2004/05/20 01:40:19 danielk1977 Exp $ +** $Id: main.c,v 1.177 2004/05/20 11:00:52 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -504,6 +504,7 @@ void sqlite3_close(sqlite *db){ } } sqlite3HashClear(&db->aFunc); + sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */ sqliteFree(db); } @@ -1033,6 +1034,164 @@ int sqlite3BtreeFactory( return sqlite3BtreeOpen(zFilename, ppBtree, nCache, btree_flags); } +const char *sqlite3_errmsg(sqlite3 *db){ + if( db->zErrMsg ){ + return db->zErrMsg; + } + return sqlite3_error_string(db->errCode); +} + +const void *sqlite3_errmsg16(sqlite3 *db){ + if( !db->zErrMsg16 ){ + char const *zErr8 = sqlite3_errmsg(db); + if( SQLITE3_BIGENDIAN ){ + db->zErrMsg16 = sqlite3utf8to16be(zErr8, -1); + }else{ + db->zErrMsg16 = sqlite3utf8to16le(zErr8, -1); + } + } + return db->zErrMsg16; +} + +int sqlite3_errcode(sqlite3 *db){ + return db->errCode; +} + +/* +** Compile the UTF-8 encoded SQL statement zSql into a statement handle. +*/ +int sqlite3_prepare( + sqlite3 *db, /* Database handle. */ + const char *zSql, /* UTF-8 encoded SQL statement. */ + int nBytes, /* Length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ + const char** pzTail /* OUT: End of parsed string */ +){ + Parse sParse; + char *zErrMsg = 0; + int rc = SQLITE_OK; + + if( sqlite3SafetyOn(db) ){ + rc = SQLITE_MISUSE; + goto prepare_out; + } + + if( !db->init.busy ){ + if( (db->flags & SQLITE_Initialized)==0 ){ + int rc, cnt = 1; + while( (rc = sqlite3Init(db, &zErrMsg))==SQLITE_BUSY + && db->xBusyCallback + && db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){} + if( rc!=SQLITE_OK ){ + goto prepare_out; + } + if( zErrMsg ){ + sqliteFree(zErrMsg); + zErrMsg = 0; + } + } + } + assert( (db->flags & SQLITE_Initialized)!=0 || db->init.busy ); + + if( db->pVdbe==0 ){ db->nChange = 0; } + memset(&sParse, 0, sizeof(sParse)); + sParse.db = db; + sqlite3RunParser(&sParse, zSql, &zErrMsg); + + if( db->xTrace && !db->init.busy ){ + /* Trace only the statment that was compiled. + ** Make a copy of that part of the SQL string since zSQL is const + ** and we must pass a zero terminated string to the trace function + ** The copy is unnecessary if the tail pointer is pointing at the + ** beginnig or end of the SQL string. + */ + if( sParse.zTail && sParse.zTail!=zSql && *sParse.zTail ){ + char *tmpSql = sqliteStrNDup(zSql, sParse.zTail - zSql); + if( tmpSql ){ + db->xTrace(db->pTraceArg, tmpSql); + free(tmpSql); + }else{ + /* If a memory error occurred during the copy, + ** trace entire SQL string and fall through to the + ** sqlite3_malloc_failed test to report the error. + */ + db->xTrace(db->pTraceArg, zSql); + } + }else{ + db->xTrace(db->pTraceArg, zSql); + } + } + + if( sqlite3_malloc_failed ){ + rc = SQLITE_NOMEM; + sqlite3RollbackAll(db); + sqlite3ResetInternalSchema(db, 0); + db->flags &= ~SQLITE_InTrans; + goto prepare_out; + } + if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; + if( sParse.rc==SQLITE_SCHEMA ){ + sqlite3ResetInternalSchema(db, 0); + } + assert( ppStmt ); + *ppStmt = (sqlite3_stmt*)sParse.pVdbe; + if( pzTail ) *pzTail = sParse.zTail; + + if( sqlite3SafetyOff(db) ){ + rc = SQLITE_MISUSE; + goto prepare_out; + } + + rc = sParse.rc; + +prepare_out: + if( zErrMsg ){ + sqlite3Error(db, rc, "%s", zErrMsg); + }else{ + sqlite3Error(db, rc, 0); + } + return rc; +} + +/* +** Compile the UTF-16 encoded SQL statement zSql into a statement handle. +*/ +int sqlite3_prepare16( + sqlite3 *db, /* Database handle. */ + const void *zSql, /* UTF-8 encoded SQL statement. */ + int nBytes, /* Length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ + const void **pzTail /* OUT: End of parsed string */ +){ + /* This function currently works by first transforming the UTF-16 + ** encoded string to UTF-8, then invoking sqlite3_prepare(). The + ** tricky bit is figuring out the pointer to return in *pzTail. + */ + char *zSql8 = 0; + char const *zTail8 = 0; + int rc; + + zSql8 = sqlite3utf16to8(zSql, nBytes); + if( !zSql8 ){ + sqlite3Error(db, SQLITE_NOMEM, 0); + return SQLITE_NOMEM; + } + rc = sqlite3_prepare(db, zSql8, -1, ppStmt, &zTail8); + + if( zTail8 && pzTail ){ + /* If sqlite3_prepare returns a tail pointer, we calculate the + ** equivalent pointer into the UTF-16 string by counting the unicode + ** characters between zSql8 and zTail8, and then returning a pointer + ** the same number of characters into the UTF-16 string. + */ + int chars_parsed = sqlite3utf8CharLen(zSql8, zTail8-zSql8); + *pzTail = (u8 *)zSql + sqlite3utf16ByteLen(zSql, chars_parsed); + } + + return rc; +} + + #if 0 /* @@ -1090,44 +1249,6 @@ int sqlite3_errcode(sqlite3 *db){ struct sqlite_stmt { }; -/* -** sqlite3_prepare -** -** TODO: error message handling -*/ -int sqlite3_prepare( - sqlite3 *db, - const char *zSql, - sqlite3_stmt **ppStmt, - const char** pzTail -){ - int rc; - rc = sqlite3_compile(db, zSql, pzTail, ppStmt, 0); - return rc; -} -int sqlite3_prepare16( - sqlite3 *db, - const void *zSql, - sqlite3_stmt **ppStmt, - const void **pzTail -){ - int rc; - char *sql8; - - sql8 = sqlite3utf16to8(zSql, -1); - if( !sql8 ){ - return SQLITE_NOMEM; - } - - /* TODO: Have to set *pzTail to point into the original UTF-16 string - ** somehow. - */ - rc = sqlite3_prepare(db, sql8, ppStmt, 0); - sqliteFree(filename8); - - return rc; -} - /* ** sqlite3_finalize */ diff --git a/src/os.h b/src/os.h index 4d01776fed..a88756f8c1 100644 --- a/src/os.h +++ b/src/os.h @@ -161,6 +161,14 @@ # define SQLITE_MIN_SLEEP_MS 17 #endif +/* +** Macros to determine whether the machine is big or little endian, +** evaluated at runtime. +*/ +static const int sqlite3_one = 1; +#define SQLITE3_BIGENDIAN (*(char *)(&sqlite3_one)==0) +#define SQLITE3_LITTLEENDIAN (*(char *)(&sqlite3_one)==1) + int sqlite3OsDelete(const char*); int sqlite3OsFileExists(const char*); int sqliteOsFileRename(const char*, const char*); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 802718cc7a..777205ee37 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.65 2004/05/20 01:40:19 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.66 2004/05/20 11:00:52 danielk1977 Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -861,7 +861,10 @@ int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out); */ int sqlite_decode_binary(const unsigned char *in, unsigned char *out); + +/* FIX ME */ typedef sqlite_vm sqlite3_stmt; +typedef sqlite sqlite3; /* ** This routine is used to bind a 32-bit integer value to a variable @@ -976,30 +979,74 @@ int sqlite3_bind_text16(sqlite3_stmt*, int i, const void *z, int, int eCopy); */ int sqlite3_bind_blob(sqlite3_stmt*, int i, const void *z, int n, int eCopy); - -#if 0 +/* +** Return the error code for the most recent sqlite3_* API call associated +** with sqlite3 handle 'db'. SQLITE_OK is returned if the most recent +** API call was successful. +** +** Calls to many sqlite3_* functions set the error code and string returned +** by sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16() +** (overwriting the previous values). A complete list of functions that set +** the error code and string returned by these functions follows. Note that +** calls to sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16() +** themselves do not affect the results of future invocations. +** +** sqlite3_bind_int32 +** sqlite3_bind_int64 +** sqlite3_bind_double +** sqlite3_bind_null +** sqlite3_bind_text +** sqlite3_bind_text16 +** sqlite3_bind_blob +** +** Assuming no other intervening sqlite3_* API calls are made, the error +** code returned by this function is associated with the same error as +** the strings returned by sqlite3_errmsg() and sqlite3_errmsg16(). +*/ +int sqlite3_errcode(sqlite3 *db); /* -** Below this point are the new sqlite3 APIs. At present these are -** implemented in terms of the sqlite2 API above. This is to get the TCL -** interface and other testing infrastructure in place for when -** functionality starts getting added. +** Return a pointer to a UTF-8 encoded string describing in english the +** error condition for the most recent sqlite3_* API call. The returned +** string is always terminated by an 0x00 byte. +** +** The string "not an error" is returned when the most recent API call was +** successful. */ +const char *sqlite3_errmsg(sqlite3*); -typedef struct sqlite sqlite3; +/* +** Return a pointer to a UTF-16 native byte order encoded string describing +** in english the error condition for the most recent sqlite3_* API call. +** The returned string is always terminated by a pair of 0x00 bytes. +** +** The string "not an error" is returned when the most recent API call was +** successful. +*/ +const void *sqlite3_errmsg16(sqlite3*); + +int sqlite3_prepare( + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement, UTF-8 encoded */ + int nBytes, /* Length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const char **pzTail /* OUT: Pointer to unused portion of zSql */ +); + +int sqlite3_prepare16( + sqlite3 *db, /* Database handle */ + const void *zSql, /* SQL statement, UTF-16 encoded */ + int nBytes, /* Length of zSql in bytes. */ + sqlite3_stmt **ppStmt, /* OUT: Statement handle */ + const void **pzTail /* OUT: Pointer to unused portion of zSql */ +); + +#if 0 int sqlite3_open(const char*, sqlite3**, const char**); int sqlite3_open16(const void*, sqlite3**, const char**); int sqlite3_close(sqlite3*); -const char *sqlite3_errmsg(sqlite3*); -const void *sqlite3_errmsg16(sqlite3*); -int sqlite3_errcode(sqlite3*); - -typedef struct sqlite3_vm sqlite3_stmt; - -int sqlite3_prepare(sqlite3*, const char*, sqlite3_stmt**, const char**); -int sqlite3_prepare16(sqlite3*, const void*, sqlite3_stmt**, const void**); int sqlite3_finalize(sqlite3_stmt*); int sqlite3_reset(sqlite3_stmt*); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f757899f40..da97159646 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.239 2004/05/20 01:12:34 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.240 2004/05/20 11:00:52 danielk1977 Exp $ */ #include "config.h" #include "sqlite.h" @@ -351,6 +351,11 @@ struct Db { ** affect the value of lsChange. ** The sqlite.csChange keeps track of the number of current changes (since ** the last statement) and is used to update sqlite_lsChange. +** +** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 +** store the most recent error code and, if applicable, string. The +** internal function sqlite3Error() is used to set these variables +** consistently. */ struct sqlite { int nDb; /* Number of backends currently in use */ @@ -397,7 +402,9 @@ struct sqlite { int nProgressOps; /* Number of opcodes for progress callback */ #endif + int errCode; /* Most recent error code (SQLITE_*) */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ + void *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ }; /* @@ -1321,7 +1328,8 @@ void *sqlite3utf8to16be(const unsigned char *pIn, int N); void *sqlite3utf8to16le(const unsigned char *pIn, int N); void sqlite3utf16to16le(void *pData, int N); void sqlite3utf16to16be(void *pData, int N); -int sqlite3utf16ByteLen(const void *pData); +int sqlite3utf16ByteLen(const void *pData, int nChar); +int sqlite3utf8CharLen(const char *pData, int nByte); int sqlite3PutVarint(unsigned char *, u64); int sqlite3GetVarint(const unsigned char *, u64 *); int sqlite3GetVarint32(const unsigned char *, u32 *); @@ -1334,3 +1342,5 @@ char const *sqlite3AffinityString(char affinity); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); char sqlite3ExprAffinity(Expr *pExpr); int sqlite3atoi64(const char*, i64*); +void sqlite3Error(sqlite *, int, const char*,...); + diff --git a/src/test1.c b/src/test1.c index 1a55fd3b2c..bff18c97f7 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.41 2004/05/20 01:12:35 danielk1977 Exp $ +** $Id: test1.c,v 1.42 2004/05/20 11:00:52 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -27,6 +27,42 @@ # define PTR_FMT "%p" #endif +static const char * errorName(int rc){ + const char *zName = 0; + switch( rc ){ + case SQLITE_OK: zName = "SQLITE_OK"; break; + case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; + case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; + case SQLITE_PERM: zName = "SQLITE_PERM"; break; + case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; + case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; + case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; + case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; + case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; + case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; + case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; + case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; + case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; + case SQLITE_FULL: zName = "SQLITE_FULL"; break; + case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; + case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; + case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; + case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; + case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; + case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; + case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; + case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; + case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; + case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; + case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; + case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; + case SQLITE_ROW: zName = "SQLITE_ROW"; break; + case SQLITE_DONE: zName = "SQLITE_DONE"; break; + default: zName = "SQLITE_Unknown"; break; + } + return zName; +} + /* ** Decode a pointer to an sqlite object. */ @@ -1192,6 +1228,185 @@ static int test_bind_blob( return TCL_OK; } +/* +** Usage: sqlite3_errcode DB +** +** Return the string representation of the most recent sqlite3_* API +** error code. e.g. "SQLITE_ERROR". +*/ +static int test_errcode( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + Tcl_SetResult(interp, (char *)errorName(sqlite3_errcode(db)), 0); + return TCL_OK; +} + +/* +** Usage: test_errmsg DB +** +** Returns the UTF-8 representation of the error message string for the +** most recent sqlite3_* API call. +*/ +static int test_errmsg( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite *db; + const char *zErr; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + + zErr = sqlite3_errmsg(db); + Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); + return TCL_OK; +} + +/* +** Usage: test_errmsg16 DB +** +** Returns the UTF-16 representation of the error message string for the +** most recent sqlite3_* API call. This is a byte array object at the TCL +** level, and it includes the 0x00 0x00 terminator bytes at the end of the +** UTF-16 string. +*/ +static int test_errmsg16( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite *db; + const void *zErr; + int bytes; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + + zErr = sqlite3_errmsg16(db); + bytes = sqlite3utf16ByteLen(zErr, -1); + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zErr, bytes)); + return TCL_OK; +} + +/* +** Usage: sqlite3_prepare DB sql bytes tailvar +** +** Compile up to bytes of the supplied SQL string using +** database handle . The parameter is the name of a global +** variable that is set to the unused portion of (if any). A +** STMT handle is returned. +*/ +static int test_prepare( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + const char *zSql; + int bytes; + const char *zTail = 0; + sqlite3_stmt *pStmt = 0; + char zBuf[50]; + + if( objc!=5 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zSql = Tcl_GetString(objv[2]); + if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; + + if( SQLITE_OK!=sqlite3_prepare(db, zSql, bytes, &pStmt, &zTail) ){ + return TCL_ERROR; + } + + if( zTail ){ + if( bytes>=0 ){ + bytes = bytes - (zTail-zSql); + } + Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0); + } + + if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; + Tcl_AppendResult(interp, zBuf, 0); + return TCL_OK; +} + +/* +** Usage: sqlite3_prepare DB sql bytes tailvar +** +** Compile up to bytes of the supplied SQL string using +** database handle . The parameter is the name of a global +** variable that is set to the unused portion of (if any). A +** STMT handle is returned. +*/ +static int test_prepare16( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + const void *zSql; + const void *zTail = 0; + Tcl_Obj *pTail = 0; + sqlite3_stmt *pStmt = 0; + char zBuf[50]; + int bytes; /* The integer specified as arg 3 */ + int objlen; /* The byte-array length of arg 2 */ + + if( objc!=5 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen); + if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; + + if( SQLITE_OK!=sqlite3_prepare16(db, zSql, bytes, &pStmt, &zTail) ){ + return TCL_ERROR; + } + + if( zTail ){ + objlen = objlen - ((u8 *)zTail-(u8 *)zSql); + }else{ + objlen = 0; + } + pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen); + Tcl_IncrRefCount(pTail); + Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0); + // Tcl_DecrRefCount(pTail); + + if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; + Tcl_AppendResult(interp, zBuf, 0); + return TCL_OK; +} + /* ** Register commands with the TCL interpreter. */ @@ -1241,6 +1456,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_bind_text", (Tcl_ObjCmdProc*)test_bind_text }, { "sqlite3_bind_text16", (Tcl_ObjCmdProc*)test_bind_text16 }, { "sqlite3_bind_blob", (Tcl_ObjCmdProc*)test_bind_blob }, + { "sqlite3_errcode", (Tcl_ObjCmdProc*)test_errcode }, + { "sqlite3_errmsg", (Tcl_ObjCmdProc*)test_errmsg }, + { "sqlite3_errmsg16", (Tcl_ObjCmdProc*)test_errmsg16 }, + { "sqlite3_prepare", (Tcl_ObjCmdProc*)test_prepare }, + { "sqlite3_prepare16", (Tcl_ObjCmdProc*)test_prepare16 }, }; int i; @@ -1264,4 +1484,3 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ } - diff --git a/src/utf.c b/src/utf.c index 461c641a39..b2891ac5e2 100644 --- a/src/utf.c +++ b/src/utf.c @@ -12,7 +12,7 @@ ** This file contains routines used to translate between UTF-8, ** UTF-16, UTF-16BE, and UTF-16LE. ** -** $Id: utf.c,v 1.5 2004/05/20 01:12:35 danielk1977 Exp $ +** $Id: utf.c,v 1.6 2004/05/20 11:00:52 danielk1977 Exp $ ** ** Notes on UTF-8: ** @@ -192,7 +192,7 @@ static int writeUtf8(UtfString *pStr, u32 code){ {0x0010FFFF, 3, 0xF7, 0xF0}, {0x00000000, 0, 0x00, 0x00} }; - struct Utf8WriteTblRow *pRow = &utf8tbl[0]; + const struct Utf8WriteTblRow *pRow = &utf8tbl[0]; while( code>pRow->max_code ){ assert( pRow->max_code ); @@ -324,17 +324,70 @@ static int writeUtf16(UtfString *pStr, int code, int big_endian){ } /* -** Return the number of bytes up to (but not including) the first \u0000 -** character in *pStr. +** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, +** return the number of unicode characters in pZ up to (but not including) +** the first 0x00 byte. If nByte is not less than zero, return the +** number of unicode characters in the first nByte of pZ (or up to +** the first 0x00, whichever comes first). */ -int sqlite3utf16ByteLen(const void *pZ){ - const unsigned char *pC1 = pZ; - const unsigned char *pC2 = pZ+1; - while( *pC1 || *pC2 ){ - pC1 += 2; - pC2 += 2; +int sqlite3utf8CharLen(const char *pZ, int nByte){ + UtfString str; + int ret = 0; + u32 code = 1; + + str.pZ = (char *)pZ; + str.n = nByte; + str.c = 0; + + while( (nByte<0 || str.c @@ -405,6 +405,48 @@ void sqlite3SetNString(char **pz, ...){ va_end(ap); } +/* +** Set the most recent error code and error string for the sqlite +** handle "db". The error code is set to "err_code". +** +** If it is not NULL, string zFormat specifies the format of the +** error string in the style of the printf functions: The following +** format characters are allowed: +** +** %s Insert a string +** %z A string that should be freed after use +** %d Insert an integer +** %T Insert a token +** %S Insert the first element of a SrcList +** +** zFormat and any string tokens that follow it are assumed to be +** encoded in UTF-8. +** +** To clear the most recent error for slqite handle "db", sqlite3Error +** should be called with err_code set to SQLITE_OK and zFormat set +** to NULL. +*/ +void sqlite3Error(sqlite *db, int err_code, const char *zFormat, ...){ + /* Free any existing error message. */ + if( db->zErrMsg ){ + sqliteFree(db->zErrMsg); + db->zErrMsg = 0; + } + if( db->zErrMsg16 ){ + sqliteFree(db->zErrMsg16); + db->zErrMsg16 = 0; + } + + /* Set the new error code and error message. */ + db->errCode = err_code; + if( zFormat ){ + va_list ap; + va_start(ap, zFormat); + db->zErrMsg = sqlite3VMPrintf(zFormat, ap); + va_end(ap); + } +} + /* ** Add an error message to pParse->zErrMsg and increment pParse->nErr. ** The following formatting characters are allowed: diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 87caeeab59..19ed035122 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -944,14 +944,20 @@ int sqlite3VdbeFinalize(Vdbe *p, char **pzErrMsg){ /* ** Unbind the value bound to variable $i in virtual machine p. This is the -** the same as binding a NULL value to the column. +** the same as binding a NULL value to the column. If the "i" parameter is +** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK. +** +** The error code stored in database p->db is overwritten with the return +** value in any case. */ static int vdbeUnbind(Vdbe *p, int i){ Mem *pVar; if( p->magic!=VDBE_MAGIC_RUN || p->pc!=0 ){ + sqlite3Error(p->db, SQLITE_MISUSE, 0); return SQLITE_MISUSE; } if( i<1 || i>p->nVar ){ + sqlite3Error(p->db, SQLITE_RANGE, 0); return SQLITE_RANGE; } i--; @@ -960,12 +966,17 @@ static int vdbeUnbind(Vdbe *p, int i){ sqliteFree(pVar->z); } pVar->flags = MEM_Null; + sqlite3Error(p->db, SQLITE_OK, 0); return SQLITE_OK; } /* ** This routine is used to bind text or blob data to an SQL variable (a ?). -** It may also be used to bind a NULL value, by setting zVal to 0. +** It may also be used to bind a NULL value, by setting zVal to 0. Any +** existing value is unbound. +** +** The error code stored in p->db is overwritten with the return value in +** all cases. */ static int vdbeBindBlob( Vdbe *p, /* Virtual machine */ @@ -976,8 +987,12 @@ static int vdbeBindBlob( int flags /* Valid combination of MEM_Blob, MEM_Str, MEM_UtfXX */ ){ Mem *pVar; + int rc; - vdbeUnbind(p, i); + rc = vdbeUnbind(p, i); + if( rc!=SQLITE_OK ){ + return rc; + } pVar = &p->apVar[i-1]; if( zVal ){ @@ -990,6 +1005,7 @@ static int vdbeBindBlob( if( bytes>NBFS ){ pVar->z = (char *)sqliteMalloc(bytes); if( !pVar->z ){ + sqlite3Error(p->db, SQLITE_NOMEM, 0); return SQLITE_NOMEM; } pVar->flags |= MEM_Dyn; @@ -1087,7 +1103,7 @@ int sqlite3_bind_text16( ** manually. In this case the variable will always be null terminated. */ if( nData<0 ){ - nData = sqlite3utf16ByteLen(zData) + 2; + nData = sqlite3utf16ByteLen(zData, -1) + 2; flags |= MEM_Term; }else{ /* If nData is greater than zero, check if the final character appears diff --git a/test/bind.test b/test/bind.test index 35a5cae83d..7c03560e33 100644 --- a/test/bind.test +++ b/test/bind.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script testing the sqlite_bind API. # -# $Id: bind.test,v 1.2 2004/05/20 01:12:35 danielk1977 Exp $ +# $Id: bind.test,v 1.3 2004/05/20 11:00:53 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -190,7 +190,32 @@ do_test bind-7.3 { } } {} -do_test bind-8.99 { +# Test that the 'out of range' error works. +do_test bind-8.1 { + catch { sqlite3_bind_null $VM 0 } +} {1} +do_test bind-8.2 { + sqlite3_errmsg $DB +} {bind index out of range} +do_test bind-8.3 { + encoding convertfrom unicode [sqlite3_errmsg16 $DB] +} {bind index out of range} +do_test bind-8.4 { + sqlite3_bind_null $VM 1 + sqlite3_errmsg $DB +} {not an error} +do_test bind-8.5 { + catch { sqlite3_bind_null $VM 4 } +} {1} +do_test bind-8.6 { + sqlite3_errmsg $DB +} {bind index out of range} +do_test bind-8.7 { + encoding convertfrom unicode [sqlite3_errmsg16 $DB] +} {bind index out of range} + + +do_test bind-9.99 { sqlite_finalize $VM } {} diff --git a/test/capi3.test b/test/capi3.test new file mode 100644 index 0000000000..a4542bfaec --- /dev/null +++ b/test/capi3.test @@ -0,0 +1,110 @@ +# 2003 January 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script testing the callback-free C/C++ API. +# +# $Id: capi3.test,v 1.1 2004/05/20 11:00:53 danielk1977 Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Return the UTF-16 representation of the supplied UTF-8 string $str. +# If $nt is true, append two 0x00 bytes as a nul terminator. +proc utf16 {str {nt 1}} { + set r [encoding convertto unicode $str] + if {$nt} { + append r "\x00\x00" + } + return $r +} + +# Return the UTF-8 representation of the supplied UTF-16 string $str. +proc utf8 {str} { + # If $str ends in two 0x00 0x00 bytes, knock these off before + # converting to UTF-8 using TCL. + binary scan $str \c* vals + if {[lindex $vals end]==0 && [lindex $vals end-1]==0} { + set str [binary format \c* [lrange $vals 0 end-2]] + } + + set r [encoding convertfrom unicode $str] + return $r +} + +# These tests complement those in capi2.test. They are organized +# as follows: +# +# capi3-1.*: Test sqlite3_prepare +# capi3-2.*: Test sqlite3_prepare16 +# + +db close +set DB [sqlite db test.db] + +do_test capi3-1.1 { + set STMT [sqlite3_prepare $DB {SELECT name FROM sqlite_master} -1 TAIL] + sqlite_finalize $STMT + set TAIL +} {} +do_test capi3-1.2 { + sqlite3_errcode $DB +} {SQLITE_OK} +do_test capi3-1.3 { + sqlite3_errmsg $DB +} {not an error} +do_test capi3-1.4 { + set sql {SELECT name FROM sqlite_master;SELECT 10} + set STMT [sqlite3_prepare $DB $sql -1 TAIL] + sqlite_finalize $STMT + set TAIL +} {SELECT 10} +do_test capi3-1.5 { + set sql {SELECT namex FROM sqlite_master} + catch { + set STMT [sqlite3_prepare $DB $sql -1 TAIL] + } +} {1} +do_test capi3-1.6 { + sqlite3_errcode $DB +} {SQLITE_ERROR} +do_test capi3-1.7 { + sqlite3_errmsg $DB +} {no such column: namex} + +do_test capi3-2.1 { + set sql16 [utf16 {SELECT name FROM sqlite_master}] + set STMT [sqlite3_prepare16 $DB $sql16 -1 ::TAIL] + sqlite_finalize $STMT + utf8 $::TAIL +} {} +do_test capi3-2.2 { + set sql [utf16 {SELECT name FROM sqlite_master;SELECT 10}] + set STMT [sqlite3_prepare16 $DB $sql -1 TAIL] + sqlite_finalize $STMT + utf8 $TAIL +} {SELECT 10} +do_test capi3-2.3 { + set sql [utf16 {SELECT namex FROM sqlite_master}] + catch { + set STMT [sqlite3_prepare16 $DB $sql -1 TAIL] + } +} {1} +do_test capi3-2.4 { + sqlite3_errcode $DB +} {SQLITE_ERROR} +do_test capi3-2.5 { + sqlite3_errmsg $DB +} {no such column: namex} + +finish_test + +