mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
added the sqlite_busy_handler() interface (CVS 109)
FossilOrigin-Name: 4fe8e51c248369572637a5351bd193f07e059fa2
This commit is contained in:
@@ -164,7 +164,7 @@ tclsqlite: $(TOP)/src/tclsqlite.c libsqlite.a
|
||||
$(TCC) $(TCL_FLAGS) -DTCLSH=1 -o tclsqlite \
|
||||
$(TOP)/src/tclsqlite.c libsqlite.a $(LIBGDBM) $(LIBTCL)
|
||||
|
||||
test: tclsqlite
|
||||
test: tclsqlite sqlite
|
||||
./tclsqlite $(TOP)/test/all.test
|
||||
|
||||
sqlite.tar.gz:
|
||||
|
54
configure
vendored
54
configure
vendored
@@ -525,7 +525,7 @@ fi
|
||||
|
||||
|
||||
# The following RCS revision string applies to configure.in
|
||||
# $Revision: 1.3 $
|
||||
# $Revision: 1.4 $
|
||||
|
||||
#########
|
||||
# Make sure we are not building in a subdirectory of the source tree.
|
||||
@@ -1727,6 +1727,58 @@ fi
|
||||
|
||||
|
||||
|
||||
#########
|
||||
# Figure out whether or not we have a "usleep()" function.
|
||||
#
|
||||
echo $ac_n "checking for usleep""... $ac_c" 1>&6
|
||||
echo "configure:1735: checking for usleep" >&5
|
||||
if eval "test \"`echo '$''{'ac_cv_func_usleep'+set}'`\" = set"; then
|
||||
echo $ac_n "(cached) $ac_c" 1>&6
|
||||
else
|
||||
cat > conftest.$ac_ext <<EOF
|
||||
#line 1740 "configure"
|
||||
#include "confdefs.h"
|
||||
/* System header to define __stub macros and hopefully few prototypes,
|
||||
which can conflict with char usleep(); below. */
|
||||
#include <assert.h>
|
||||
/* Override any gcc2 internal prototype to avoid an error. */
|
||||
/* We use char because int might match the return type of a gcc2
|
||||
builtin and then its argument prototype would still apply. */
|
||||
char usleep();
|
||||
|
||||
int main() {
|
||||
|
||||
/* The GNU C library defines this for functions which it implements
|
||||
to always fail with ENOSYS. Some functions are actually named
|
||||
something starting with __ and the normal name is an alias. */
|
||||
#if defined (__stub_usleep) || defined (__stub___usleep)
|
||||
choke me
|
||||
#else
|
||||
usleep();
|
||||
#endif
|
||||
|
||||
; return 0; }
|
||||
EOF
|
||||
if { (eval echo configure:1763: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
|
||||
rm -rf conftest*
|
||||
eval "ac_cv_func_usleep=yes"
|
||||
else
|
||||
echo "configure: failed program was:" >&5
|
||||
cat conftest.$ac_ext >&5
|
||||
rm -rf conftest*
|
||||
eval "ac_cv_func_usleep=no"
|
||||
fi
|
||||
rm -f conftest*
|
||||
fi
|
||||
|
||||
if eval "test \"`echo '$ac_cv_func_'usleep`\" = yes"; then
|
||||
echo "$ac_t""yes" 1>&6
|
||||
TARGET_CFLAGS="$TARGET_CFLAGS -DHAVE_USLEEP=1"
|
||||
else
|
||||
echo "$ac_t""no" 1>&6
|
||||
fi
|
||||
|
||||
|
||||
#########
|
||||
# Generate the output files.
|
||||
#
|
||||
|
@@ -151,7 +151,7 @@ AC_INIT(src/sqlite.h)
|
||||
dnl Put the RCS revision string after AC_INIT so that it will also
|
||||
dnl show in in configure.
|
||||
# The following RCS revision string applies to configure.in
|
||||
# $Revision: 1.3 $
|
||||
# $Revision: 1.4 $
|
||||
|
||||
#########
|
||||
# Make sure we are not building in a subdirectory of the source tree.
|
||||
@@ -509,6 +509,11 @@ fi
|
||||
AC_SUBST(TARGET_READLINE_INC)
|
||||
AC_SUBST(TARGET_HAVE_READLINE)
|
||||
|
||||
#########
|
||||
# Figure out whether or not we have a "usleep()" function.
|
||||
#
|
||||
AC_CHECK_FUNC(usleep, [TARGET_CFLAGS="$TARGET_CFLAGS -DHAVE_USLEEP=1"])
|
||||
|
||||
#########
|
||||
# Generate the output files.
|
||||
#
|
||||
|
43
manifest
43
manifest
@@ -1,29 +1,29 @@
|
||||
C :-)\s(CVS\s108)
|
||||
D 2000-06-26T12:02:51
|
||||
C added\sthe\ssqlite_busy_handler()\sinterface\s(CVS\s109)
|
||||
D 2000-07-28T14:32:48
|
||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||
F Makefile.in 02ecb0cd0de7ddf7b4623d480061870798787556
|
||||
F Makefile.in 9e6dcd232e594fb599a5e9ba8bcf45e6c6e2fe72
|
||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||
F configure c366a0402bce79ef11fe1bf703ad6ce4ff6afbb0 x
|
||||
F configure.in 1085ff994a334b131325de906ed318e926673588
|
||||
F configure 1354f60305c781609c94cd4b677ab4ff4d830b85 x
|
||||
F configure.in 77732d0a7d3ec66b7fc2303ae823fa5419ca7e6c
|
||||
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||
F src/build.c 55edb404bbf4476c73c81604ddb9738281a689a4
|
||||
F src/dbbe.c 99aa6daca9a039eebb284dd459bef712ea3843f9
|
||||
F src/build.c ac2e238356008411c3aa09c96823529d4103afcc
|
||||
F src/dbbe.c 3604cf7dec6856a4963ab8f2220449f3d02e759a
|
||||
F src/dbbe.h 8718b718b36d37584e9bbdfccec10588fa91271f
|
||||
F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065
|
||||
F src/expr.c 2fa63f086707176d09092e71832f9bbdc6a8ac85
|
||||
F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
|
||||
F src/main.c 30b33b6e0cdd5ae1c0af9f626e78a1dc7b835e26
|
||||
F src/main.c 82dba47063cb9837910c3bcefacb47de7486fb47
|
||||
F src/parse.y 86e268c29a0f00ffc062bbe934d95ea0d6308b0a
|
||||
F src/select.c aaf23d4a6ef44e4378840ec94b6aa64641c01b5c
|
||||
F src/shell.c 8387580e44878022c88c02b189bf23bff1862bda
|
||||
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
||||
F src/sqliteInt.h ddc6f8081ef469ede272cf6a382773dac5758dfc
|
||||
F src/shell.c ffcb11569f6f1756148b389ac0f1fc480859698e
|
||||
F src/sqlite.h 82ae53028e27919250f886ff9d7c4927de81978a
|
||||
F src/sqliteInt.h cf4b8f3c7fbb50adf3d879770defe72502a39022
|
||||
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
||||
F src/tokenize.c 77ff8164a8751994bc9926ce282847f653ac0c16
|
||||
F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc
|
||||
F src/util.c fcd7ac9d2be8353f746e52f665e6c4f5d6b3b805
|
||||
F src/vdbe.c 38cec3e88db70b7689018377c1594ac18f746b19
|
||||
F src/vdbe.h 5f58611b19799de2dbcdefa4eef33a255cfa8d0d
|
||||
F src/vdbe.c 72b533a452953aca618a935b5155d1d4eed3193c
|
||||
F src/vdbe.h 6c5653241633c583549c2d8097394ab52550eb63
|
||||
F src/where.c 420f666a38b405cd58bd7af832ed99f1dbc7d336
|
||||
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
|
||||
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
|
||||
@@ -34,6 +34,7 @@ F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603
|
||||
F test/index.test 620ceab7165dd078d1266bdc2cac6147f04534ac
|
||||
F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
|
||||
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
|
||||
F test/lock.test 42a2d171eba1078cf3fd58ab64241eb8f1b08d69
|
||||
F test/main.test b7366cc6f3690915a11834bc1090deeff08acaf9
|
||||
F test/select1.test 4e57b0b5eae0c991d9cc51d1288be0476110e6f6
|
||||
F test/select2.test ed6e7fc3437079686d7ae4390a00347bbd5f7bf8
|
||||
@@ -57,15 +58,15 @@ F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
|
||||
F www/arch.fig 4e26e9dca3c49724fc8f554c695ddea9f2413156
|
||||
F www/arch.png c4d908b79065a72e7dcf19317f36d1324c550e87
|
||||
F www/arch.tcl 4f6a9afecc099a27bba17b4f8cc9561abc15dc40
|
||||
F www/c_interface.tcl 8eb800f67e6896b1894d666b81c0b418cea09fc7
|
||||
F www/changes.tcl dc7ae83bf05845c043c6d2315413f2dae989658d
|
||||
F www/c_interface.tcl 29593cf77025bab137b7ba64b9459eb5eb6b4873
|
||||
F www/changes.tcl 6c14cc0f1c1a8929aa0b44304b0e2450d801b5b5
|
||||
F www/fileformat.tcl f3a70650e942262f8285d53097d48f0b3aa59862
|
||||
F www/index.tcl 4116afce6a8c63d68882d2b00aa10b079e0129cd
|
||||
F www/index.tcl 58c9a33ceba12f5efee446c6b10b4f6523a214e1
|
||||
F www/lang.tcl 1645e9107d75709be4c6099b643db235bbe0a151
|
||||
F www/opcode.tcl 401bdc639509c2f17d3bb97cbbdfdc22a61faa07
|
||||
F www/sqlite.tcl b685dc3ce345a6db0441e6d5716ed29abb96dd29
|
||||
F www/vdbe.tcl 3ea62769f7a09ee0ee803c8de000182909a31e4e
|
||||
P 79ce59cf79df3da2c9dcb944dba15c64c99fbad1
|
||||
R 7f9d7add7ab2d3c72acbddbd24bcb674
|
||||
F www/sqlite.tcl 69781eaffb02e17aa4af28b76a2bedb19baa8e9f
|
||||
F www/vdbe.tcl 3330c700ef9c212a169f568a595361e4cce749ed
|
||||
P 937c27b7e18505d0f8b85d2040db8d6a8b7cd441
|
||||
R c695a62141a00b7f6ab88cfdecb913c6
|
||||
U drh
|
||||
Z 7a87be763e4ccb46d3ff76e2d3d669ce
|
||||
Z 65d7af2246a10e3639543f77b1956b6b
|
||||
|
@@ -1 +1 @@
|
||||
937c27b7e18505d0f8b85d2040db8d6a8b7cd441
|
||||
4fe8e51c248369572637a5351bd193f07e059fa2
|
@@ -33,7 +33,7 @@
|
||||
** COPY
|
||||
** VACUUM
|
||||
**
|
||||
** $Id: build.c,v 1.19 2000/06/21 13:59:11 drh Exp $
|
||||
** $Id: build.c,v 1.20 2000/07/28 14:32:49 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -56,7 +56,8 @@ void sqliteExec(Parse *pParse){
|
||||
FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
|
||||
sqliteVdbeTrace(pParse->pVdbe, trace);
|
||||
sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
|
||||
&pParse->zErrMsg);
|
||||
&pParse->zErrMsg, pParse->db->pBusyArg,
|
||||
pParse->db->xBusyCallback);
|
||||
}
|
||||
sqliteVdbeDelete(pParse->pVdbe);
|
||||
pParse->pVdbe = 0;
|
||||
|
@@ -30,7 +30,7 @@
|
||||
** relatively simple to convert to a different database such
|
||||
** as NDBM, SDBM, or BerkeleyDB.
|
||||
**
|
||||
** $Id: dbbe.c,v 1.15 2000/06/21 13:59:11 drh Exp $
|
||||
** $Id: dbbe.c,v 1.16 2000/07/28 14:32:49 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <gdbm.h>
|
||||
@@ -382,7 +382,12 @@ int sqliteDbbeOpenCursor(
|
||||
pCursr->pFile = pFile;
|
||||
pCursr->readPending = 0;
|
||||
pCursr->needRewind = 1;
|
||||
*ppCursr = pCursr;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteDbbeCloseCursor(pCursr);
|
||||
*ppCursr = 0;
|
||||
}else{
|
||||
*ppCursr = pCursr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
73
src/main.c
73
src/main.c
@@ -26,7 +26,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.13 2000/06/21 13:59:12 drh Exp $
|
||||
** $Id: main.c,v 1.14 2000/07/28 14:32:49 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -134,7 +134,8 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
|
||||
return 1;
|
||||
}
|
||||
sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
|
||||
rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
|
||||
rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg,
|
||||
db->pBusyArg, db->xBusyCallback);
|
||||
sqliteVdbeDelete(vdbe);
|
||||
if( rc==SQLITE_OK ){
|
||||
Table *pTab;
|
||||
@@ -276,3 +277,71 @@ int sqlite_exec(
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine implements a busy callback that sleeps and tries
|
||||
** again until a timeout value is reached. The timeout value is
|
||||
** an integer number of milliseconds passed in as the first
|
||||
** argument.
|
||||
*/
|
||||
static int sqlite_default_busy_callback(
|
||||
void *Timeout, /* Maximum amount of time to wait */
|
||||
const char *NotUsed, /* The name of the table that is busy */
|
||||
int count /* Number of times table has been busy */
|
||||
){
|
||||
int rc;
|
||||
#ifdef HAVE_USLEEP
|
||||
int delay = 10000;
|
||||
int prior_delay = 0;
|
||||
int timeout = (int)Timeout;
|
||||
int i;
|
||||
|
||||
for(i=1; i<count; i++){
|
||||
prior_delay += delay;
|
||||
delay = delay*2;
|
||||
if( delay>=1000000 ){
|
||||
delay = 1000000;
|
||||
prior_delay += 1000000*(count - i - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( prior_delay + delay > timeout*1000 ){
|
||||
delay = timeout*1000 - prior_delay;
|
||||
if( delay<=0 ) return 0;
|
||||
}
|
||||
usleep(delay);
|
||||
return 1;
|
||||
#else
|
||||
int timeout = (int)Timeout;
|
||||
if( (count+1)*1000 > timeout ){
|
||||
return 0;
|
||||
}
|
||||
sleep(1);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine sets the busy callback for an Sqlite database to the
|
||||
** given callback function with the given argument.
|
||||
*/
|
||||
void sqlite_busy_handler(
|
||||
sqlite *db,
|
||||
int (*xBusy)(void*,const char*,int),
|
||||
void *pArg
|
||||
){
|
||||
db->xBusyCallback = xBusy;
|
||||
db->pBusyArg = pArg;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine installs a default busy handler that waits for the
|
||||
** specified number of milliseconds before returning 0.
|
||||
*/
|
||||
void sqlite_busy_timeout(sqlite *db, int ms){
|
||||
if( ms>0 ){
|
||||
sqlite_busy_handler(db, sqlite_default_busy_callback, (void*)ms);
|
||||
}else{
|
||||
sqlite_busy_handler(db, 0, 0);
|
||||
}
|
||||
}
|
||||
|
11
src/shell.c
11
src/shell.c
@@ -24,7 +24,7 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.15 2000/06/21 13:59:12 drh Exp $
|
||||
** $Id: shell.c,v 1.16 2000/07/28 14:32:49 drh Exp $
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -112,7 +112,7 @@ static char *one_input_line(const char *zPrior, int isatty){
|
||||
zPrompt = "sqlite> ";
|
||||
}
|
||||
zResult = readline(zPrompt);
|
||||
add_history(zResult);
|
||||
if( zResult ) add_history(zResult);
|
||||
return zResult;
|
||||
}
|
||||
|
||||
@@ -382,6 +382,7 @@ static char zHelp[] =
|
||||
".schema ?TABLE? Show the CREATE statements\n"
|
||||
".separator STRING Change separator string for \"list\" mode\n"
|
||||
".tables List names all tables in the database\n"
|
||||
".timeout MS Try opening locked tables for MS milliseconds\n"
|
||||
".width NUM NUM ... Set column widths for \"column\" mode\n"
|
||||
;
|
||||
|
||||
@@ -554,7 +555,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
|
||||
sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
|
||||
}else
|
||||
|
||||
if( c=='t' && strncmp(azArg[0], "tables", n)==0 ){
|
||||
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
|
||||
struct callback_data data;
|
||||
char *zErrMsg = 0;
|
||||
static char zSql[] =
|
||||
@@ -569,6 +570,10 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
|
||||
sqlite_busy_timeout(db, atoi(azArg[1]));
|
||||
}else
|
||||
|
||||
if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
|
||||
int j;
|
||||
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
|
||||
|
52
src/sqlite.h
52
src/sqlite.h
@@ -24,7 +24,7 @@
|
||||
** This header file defines the interface that the sqlite library
|
||||
** presents to client programs.
|
||||
**
|
||||
** @(#) $Id: sqlite.h,v 1.3 2000/06/02 01:51:20 drh Exp $
|
||||
** @(#) $Id: sqlite.h,v 1.4 2000/07/28 14:32:50 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_H_
|
||||
#define _SQLITE_H_
|
||||
@@ -94,12 +94,16 @@ typedef int (*sqlite_callback)(void*,int,char**, char**);
|
||||
** message is written into memory obtained from malloc() and
|
||||
** *errmsg is made to point to that message. If errmsg==NULL,
|
||||
** then no error message is ever written. The return value is
|
||||
** SQLITE_ERROR if an error occurs.
|
||||
** SQLITE_ERROR if an error occurs. The calling function is
|
||||
** responsible for freeing the memory that holds the error
|
||||
** message.
|
||||
**
|
||||
** If the query could not be executed because a database file is
|
||||
** locked or busy, then this function returns SQLITE_BUSY. If
|
||||
** the query could not be executed because a file is missing or
|
||||
** has incorrect permissions, this function returns SQLITE_ERROR.
|
||||
** locked or busy, then this function returns SQLITE_BUSY. (This
|
||||
** behavior can be modified somewhat using the sqlite_busy_handler()
|
||||
** and sqlite_busy_timeout() functions below.) If the query could
|
||||
** not be executed because a file is missing or has incorrect
|
||||
** permissions, this function returns SQLITE_ERROR.
|
||||
*/
|
||||
int sqlite_exec(
|
||||
sqlite*, /* An open database */
|
||||
@@ -121,8 +125,6 @@ int sqlite_exec(
|
||||
#define SQLITE_NOMEM 6 /* A malloc() failed */
|
||||
#define SQLITE_READONLY 7 /* Attempt to write a readonly database */
|
||||
|
||||
|
||||
|
||||
/* This function returns true if the given input string comprises
|
||||
** one or more complete SQL statements.
|
||||
**
|
||||
@@ -132,4 +134,40 @@ int sqlite_exec(
|
||||
*/
|
||||
int sqlite_complete(const char *sql);
|
||||
|
||||
/*
|
||||
** This routine identifies a callback function that is invoked
|
||||
** whenever an attempt is made to open a database table that is
|
||||
** currently locked by another process or thread. If the busy callback
|
||||
** is NULL, then sqlite_exec() returns SQLITE_BUSY immediately if
|
||||
** it finds a locked table. If the busy callback is not NULL, then
|
||||
** sqlite_exec() invokes the callback with three arguments. The
|
||||
** second argument is the name of the locked table and the third
|
||||
** argument is the number of times the table has been busy. If the
|
||||
** busy callback returns 0, then sqlite_exec() immediately returns
|
||||
** SQLITE_BUSY. If the callback returns non-zero, then sqlite_exec()
|
||||
** tries to open the table again and the cycle repeats.
|
||||
**
|
||||
** The default busy callback is NULL.
|
||||
**
|
||||
** Sqlite is re-entrant, so the busy handler may start a new query.
|
||||
** (It is not clear why anyone would every want to do this, but it
|
||||
** is allowed, in theory.) But the busy handler may not close the
|
||||
** database. Closing the database from a busy handler will delete
|
||||
** data structures out from under the executing query and will
|
||||
** probably result in a coredump.
|
||||
*/
|
||||
void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*);
|
||||
|
||||
/*
|
||||
** This routine sets a busy handler that sleeps for a while when a
|
||||
** table is locked. The handler will sleep multiple times until
|
||||
** at least "ms" milleseconds of sleeping have been done. After
|
||||
** "ms" milleseconds of sleeping, the handler returns 0 which
|
||||
** causes sqlite_exec() to return SQLITE_BUSY.
|
||||
**
|
||||
** Calling this routine with an argument less than or equal to zero
|
||||
** turns off all busy handlers.
|
||||
*/
|
||||
void sqlite_busy_timeout(sqlite*, int ms);
|
||||
|
||||
#endif /* _SQLITE_H_ */
|
||||
|
@@ -23,7 +23,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.25 2000/06/21 13:59:12 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.26 2000/07/28 14:32:50 drh Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "dbbe.h"
|
||||
@@ -122,6 +122,8 @@ typedef struct AggExpr AggExpr;
|
||||
struct sqlite {
|
||||
Dbbe *pBe; /* The backend driver */
|
||||
int flags; /* Miscellanous flags */
|
||||
void *pBusyArg; /* 1st Argument to the busy callback */
|
||||
int (*xBusyCallback)(void *,const char*,int);
|
||||
Table *apTblHash[N_HASH]; /* All tables of the database */
|
||||
Index *apIdxHash[N_HASH]; /* All indices of the database */
|
||||
};
|
||||
|
63
src/vdbe.c
63
src/vdbe.c
@@ -41,7 +41,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.34 2000/06/21 13:59:13 drh Exp $
|
||||
** $Id: vdbe.c,v 1.35 2000/07/28 14:32:50 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <unistd.h>
|
||||
@@ -868,13 +868,22 @@ static Sorter *Merge(Sorter *pLeft, Sorter *pRight){
|
||||
** Other fatal errors return SQLITE_ERROR.
|
||||
**
|
||||
** If a database file could not be opened because it is locked by
|
||||
** another database instance, then this routine returns SQLITE_BUSY.
|
||||
** another database instance, then the xBusy() callback is invoked
|
||||
** with pBusyArg as its first argument, the name of the table as the
|
||||
** second argument, and the number of times the open has been attempted
|
||||
** as the third argument. The xBusy() callback will typically wait
|
||||
** for the database file to be openable, then return. If xBusy()
|
||||
** returns non-zero, another attempt is made to open the file. If
|
||||
** xBusy() returns zero, or if xBusy is NULL, then execution halts
|
||||
** and this routine returns SQLITE_BUSY.
|
||||
*/
|
||||
int sqliteVdbeExec(
|
||||
Vdbe *p, /* The VDBE */
|
||||
sqlite_callback xCallback, /* The callback */
|
||||
void *pArg, /* 1st argument to callback */
|
||||
char **pzErrMsg /* Error msg written here */
|
||||
char **pzErrMsg, /* Error msg written here */
|
||||
void *pBusyArg, /* 1st argument to the busy callback */
|
||||
int (*xBusy)(void*,const char*,int) /* Called when a file is busy */
|
||||
){
|
||||
int pc; /* The program counter */
|
||||
Op *pOp; /* Current operation */
|
||||
@@ -1698,6 +1707,7 @@ int sqliteVdbeExec(
|
||||
** deleted when the cursor is closed.
|
||||
*/
|
||||
case OP_Open: {
|
||||
int busy = 0;
|
||||
int i = pOp->p1;
|
||||
if( i<0 ) goto bad_instruction;
|
||||
if( i>=p->nCursor ){
|
||||
@@ -1709,26 +1719,35 @@ int sqliteVdbeExec(
|
||||
}else if( p->aCsr[i].pCursor ){
|
||||
sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
|
||||
}
|
||||
rc = sqliteDbbeOpenCursor(p->pBe, pOp->p3, pOp->p2,&p->aCsr[i].pCursor);
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
sqliteSetString(pzErrMsg,"table ", pOp->p3, " is locked", 0);
|
||||
break;
|
||||
do {
|
||||
rc = sqliteDbbeOpenCursor(p->pBe,pOp->p3,pOp->p2,&p->aCsr[i].pCursor);
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){
|
||||
sqliteSetString(pzErrMsg,"table ", pOp->p3, " is locked", 0);
|
||||
busy = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_PERM: {
|
||||
sqliteSetString(pzErrMsg, pOp->p2 ? "write" : "read",
|
||||
" permission denied for table ", pOp->p3, 0);
|
||||
break;
|
||||
}
|
||||
case SQLITE_READONLY: {
|
||||
sqliteSetString(pzErrMsg,"table ", pOp->p3,
|
||||
" is readonly", 0);
|
||||
break;
|
||||
}
|
||||
case SQLITE_NOMEM: {
|
||||
goto no_mem;
|
||||
}
|
||||
case SQLITE_OK: {
|
||||
busy = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SQLITE_PERM: {
|
||||
sqliteSetString(pzErrMsg, pOp->p2 ? "write" : "read",
|
||||
" permission denied for table ", pOp->p3, 0);
|
||||
break;
|
||||
}
|
||||
case SQLITE_READONLY: {
|
||||
sqliteSetString(pzErrMsg,"table ", pOp->p3,
|
||||
" is readonly", 0);
|
||||
break;
|
||||
}
|
||||
case SQLITE_NOMEM: {
|
||||
goto no_mem;
|
||||
}
|
||||
}
|
||||
}while( busy );
|
||||
p->aCsr[i].index = 0;
|
||||
p->aCsr[i].keyAsData = 0;
|
||||
break;
|
||||
|
@@ -27,7 +27,7 @@
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.10 2000/06/11 23:50:13 drh Exp $
|
||||
** $Id: vdbe.h,v 1.11 2000/07/28 14:32:50 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
@@ -187,7 +187,8 @@ void sqliteVdbeDequoteP3(Vdbe*, int addr);
|
||||
int sqliteVdbeMakeLabel(Vdbe*);
|
||||
void sqliteVdbeDelete(Vdbe*);
|
||||
int sqliteVdbeOpcode(const char *zName);
|
||||
int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**);
|
||||
int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**,void*,
|
||||
int(*)(void*,const char*,int));
|
||||
int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
|
||||
void sqliteVdbeResolveLabel(Vdbe*, int);
|
||||
int sqliteVdbeCurrentAddr(Vdbe*);
|
||||
|
101
test/lock.test
Normal file
101
test/lock.test
Normal file
@@ -0,0 +1,101 @@
|
||||
# Copyright (c) 1999, 2000 D. Richard Hipp
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# Author contact information:
|
||||
# drh@hwaci.com
|
||||
# http://www.hwaci.com/drh/
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is database locks.
|
||||
#
|
||||
# $Id: lock.test,v 1.1 2000/07/28 14:32:50 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
# Create a largish table
|
||||
#
|
||||
do_test lock-1.0 {
|
||||
execsql {CREATE TABLE big(f1 int, f2 int, f3 int)}
|
||||
set f [open ./testdata1.txt w]
|
||||
for {set i 1} {$i<=500} {incr i} {
|
||||
puts $f "$i\t[expr {$i*2}]\t[expr {$i*3}]"
|
||||
}
|
||||
close $f
|
||||
execsql {COPY big FROM './testdata1.txt'}
|
||||
file delete -force ./testdata1.txt
|
||||
} {}
|
||||
|
||||
do_test lock-1.1 {
|
||||
# Create a background query that gives us a read lock on the big table
|
||||
#
|
||||
set f [open slow.sql w]
|
||||
puts $f "SELECT a.f1, b.f1 FROM big AS a, big AS B"
|
||||
puts $f "WHERE a.f1+b.f1==0.5;"
|
||||
close $f
|
||||
set ::lock_pid [exec ./sqlite testdb <slow.sql &]
|
||||
after 10
|
||||
set v {}
|
||||
} {}
|
||||
|
||||
do_test lock-1.2 {
|
||||
# Now try to update the database
|
||||
#
|
||||
set v [catch {execsql {UPDATE big SET f2='xyz' WHERE f1=11}} msg]
|
||||
lappend v $msg
|
||||
} {1 {table big is locked}}
|
||||
|
||||
do_test lock-1.3 {
|
||||
# Try to update the database in a separate process
|
||||
#
|
||||
set f [open update.sql w]
|
||||
puts $f ".timeout 0"
|
||||
puts $f "UPDATE big SET f2='xyz' WHERE f1=11;"
|
||||
puts $f "SELECT f2 FROM big WHERE f1=11;"
|
||||
close $f
|
||||
exec ./sqlite testdb <update.sql
|
||||
} "SQL error: table big is locked\n22"
|
||||
|
||||
do_test lock-1.4 {
|
||||
# Try to update the database using a timeout
|
||||
#
|
||||
set f [open update.sql w]
|
||||
puts $f ".timeout 1000"
|
||||
puts $f "UPDATE big SET f2='xyz' WHERE f1=11;"
|
||||
puts $f "SELECT f2 FROM big WHERE f1=11;"
|
||||
close $f
|
||||
exec ./sqlite testdb <update.sql
|
||||
} "SQL error: table big is locked\n22"
|
||||
|
||||
do_test lock-1.5 {
|
||||
# Try to update the database using a timeout
|
||||
#
|
||||
set f [open update.sql w]
|
||||
puts $f ".timeout 10000"
|
||||
puts $f "UPDATE big SET f2='xyz' WHERE f1=11;"
|
||||
puts $f "SELECT f2 FROM big WHERE f1=11;"
|
||||
close $f
|
||||
exec ./sqlite testdb <update.sql
|
||||
} {xyz}
|
||||
|
||||
catch {exec ps -uax | grep $::lock_pid}
|
||||
catch {exec kill -HUP $::lock_pid}
|
||||
catch {exec kill -9 $::lock_pid}
|
||||
|
||||
finish_test
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this Tcl script to generate the sqlite.html file.
|
||||
#
|
||||
set rcsid {$Id: c_interface.tcl,v 1.5 2000/06/21 13:59:14 drh Exp $}
|
||||
set rcsid {$Id: c_interface.tcl,v 1.6 2000/07/28 14:32:51 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head>
|
||||
@@ -22,7 +22,8 @@ programming interface.</p>
|
||||
|
||||
<h2>The API</h2>
|
||||
|
||||
<p>The interface to the SQLite library consists of 4 functions,
|
||||
<p>The interface to the SQLite library consists of six functions
|
||||
(only three of which are required),
|
||||
one opaque data structure, and some constants used as return
|
||||
values from sqlite_exec():</p>
|
||||
|
||||
@@ -43,6 +44,10 @@ int sqlite_exec(
|
||||
|
||||
int sqlite_complete(const char *sql);
|
||||
|
||||
void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);
|
||||
|
||||
void sqlite_busy_timeout(sqlite*, int ms);
|
||||
|
||||
#define SQLITE_OK 0 /* Successful result */
|
||||
#define SQLITE_INTERNAL 1 /* An internal logic error in SQLite */
|
||||
#define SQLITE_ERROR 2 /* SQL error or missing database */
|
||||
@@ -225,6 +230,47 @@ then <b>sqlite_exec()</b> is called and the input buffer is reset. If
|
||||
the continuation prompt and another line of text is read and added to
|
||||
the input buffer.</p>
|
||||
|
||||
<h2>Changing the libraries reponse to locked files</h2>
|
||||
|
||||
<p>The GDBM library supports database locks at the file level.
|
||||
If a GDBM database file is opened for reading, then that same
|
||||
file cannot be reopened for writing until all readers have closed
|
||||
the file. If a GDBM file is open for writing, then the file cannot
|
||||
be reopened for reading or writing until it is closed.</p>
|
||||
|
||||
<p>If the SQLite library attempts to open a GDBM file and finds that
|
||||
the file is locked, the default action is to abort the current
|
||||
operation and return SQLITE_BUSY. But this is not always the most
|
||||
convenient behavior, so a mechanism exists to change it.</p>
|
||||
|
||||
<p>The <b>sqlite_busy_handler()</b> procedure can be used to register
|
||||
a busy callback with an open SQLite database. The busy callback will
|
||||
be invoked whenever SQLite tries to open a GDBM file that is locked.
|
||||
The callback will typically do some other useful work, or perhaps sleep,
|
||||
in order to give the lock a chance to clear. If the callback returns
|
||||
non-zero, then SQLite tries again to open the GDBM file and the cycle
|
||||
repeats. If the callback returns zero, then SQLite aborts the current
|
||||
operation and returns SQLITE_BUSY.</p>
|
||||
|
||||
<p>The arguments to <b>sqlite_busy_handler()</b> are the opaque
|
||||
structure returned from <b>sqlite_open()</b>, a pointer to the busy
|
||||
callback function, and a generic pointer that will be passed as
|
||||
the first argument to the busy callback. When SQLite invokes the
|
||||
busy callback, it sends it three arguments: the generic pointer
|
||||
that was passed in as the third argument to <b>sqlite_busy_handler</b>,
|
||||
the name of the database table or index that the library is trying
|
||||
to open, and the number of times that the library has attempted to
|
||||
open the database table or index.</p>
|
||||
|
||||
<p>For the common case where we want the busy callback to sleep,
|
||||
the SQLite library provides a convenience routine <b>sqlite_busy_timeout()</b>.
|
||||
The first argument to <b>sqlite_busy_timeout()</b> is a pointer to
|
||||
an open SQLite database and the second argument is a number of milliseconds.
|
||||
After <b>sqlite_busy_timeout()</b> has been executed, the SQLite library
|
||||
will wait for the lock to clear for at least the number of milliseconds
|
||||
specified before it returns SQLITE_BUSY. Specifying zero milliseconds for
|
||||
the timeout restores the default behavior.</p>
|
||||
|
||||
<h2>Usage Examples</h2>
|
||||
|
||||
<p>For examples of how the SQLite C/C++ interface can be used,
|
||||
|
@@ -17,6 +17,11 @@ proc chng {date desc} {
|
||||
puts "<DD><P><UL>$desc</UL></P></DD>"
|
||||
}
|
||||
|
||||
chng {2000 July 28} {
|
||||
<li>Added the <b>sqlite_busy_handler()</b>
|
||||
and <b>sqlite_busy_timeout()</b> interface.</li>
|
||||
}
|
||||
|
||||
chng {2000 June 23} {
|
||||
<li>Begin writing the <a href="vdbe.html">VDBE tutorial</a>.</li>
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this TCL script to generate HTML for the index.html file.
|
||||
#
|
||||
set rcsid {$Id: index.tcl,v 1.18 2000/06/09 14:14:34 drh Exp $}
|
||||
set rcsid {$Id: index.tcl,v 1.19 2000/07/28 14:32:51 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head><title>SQLite: An SQL Database Engine Built Atop GDBM</title></head>
|
||||
@@ -50,8 +50,8 @@ an separate RDBMS.</p>
|
||||
<li>Import and export data from
|
||||
<a href="http://www.postgresql.org/">PostgreSQL</a>.</li>
|
||||
<li>Very simple
|
||||
<a href="c_interface.html">C/C++ interface</a> uses only
|
||||
four functions and one opaque structure.</li>
|
||||
<a href="c_interface.html">C/C++ interface</a> requires the use of only
|
||||
three functions and one opaque structure.</li>
|
||||
<li>A <a href="http://dev.scriptics.com/">Tcl</a> interface is
|
||||
included.</li>
|
||||
<li>Command-line access program <a href="sqlite.html">sqlite</a> uses
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this Tcl script to generate the sqlite.html file.
|
||||
#
|
||||
set rcsid {$Id: sqlite.tcl,v 1.10 2000/06/23 17:02:09 drh Exp $}
|
||||
set rcsid {$Id: sqlite.tcl,v 1.11 2000/07/28 14:32:51 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head>
|
||||
@@ -156,6 +156,7 @@ sqlite> (((.help)))
|
||||
.schema ?TABLE? Show the CREATE statements
|
||||
.separator STRING Change separator string for "list" mode
|
||||
.tables List names all tables in the database
|
||||
.timeout MS Try opening locked tables for MS milliseconds
|
||||
.width NUM NUM ... Set column widths for "column" mode
|
||||
sqlite>
|
||||
}
|
||||
@@ -467,6 +468,13 @@ addr opcode p1 p2 p3
|
||||
}
|
||||
|
||||
puts {
|
||||
|
||||
<p>The ".timeout" command sets the amount of time that the <b>sqlite</b>
|
||||
program will wait for locks to clear on files it is trying to access
|
||||
before returning an error. The default value of the timeout is zero so
|
||||
that an error is returned immediately if any needed database table or
|
||||
index is locked.</p>
|
||||
|
||||
<p>And finally, we mention the ".exit" command which causes the
|
||||
sqlite program to exit.</p>
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Run this Tcl script to generate the vdbe.html file.
|
||||
#
|
||||
set rcsid {$Id: vdbe.tcl,v 1.3 2000/06/26 12:02:51 drh Exp $}
|
||||
set rcsid {$Id: vdbe.tcl,v 1.4 2000/07/28 14:32:51 drh Exp $}
|
||||
|
||||
puts {<html>
|
||||
<head>
|
||||
@@ -728,7 +728,7 @@ created for every SQLite database. It looks like this:</p>
|
||||
<blockquote><pre>
|
||||
CREATE TABLE sqlite_master (
|
||||
type TEXT, -- either "table" or "index"
|
||||
name TEXT, -- name of the table or index
|
||||
name TEXT, -- name of this table or index
|
||||
tbl_name TEXT, -- for indices: name of associated table
|
||||
sql TEXT -- SQL text of the original CREATE statement
|
||||
)
|
||||
@@ -751,7 +751,7 @@ the first thing it does is a SELECT to read the "sql"
|
||||
columns from all entries of the sqlite_master table.
|
||||
The "sql" column contains the complete SQL text of the
|
||||
CREATE statement that originally generated the index or
|
||||
table. This text is fed back into the SQLite parse
|
||||
table. This text is fed back into the SQLite parser
|
||||
and used to reconstruct the
|
||||
internal data structures describing the index or table.</p>
|
||||
|
||||
|
Reference in New Issue
Block a user