1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

added the sqlite_busy_handler() interface (CVS 109)

FossilOrigin-Name: 4fe8e51c248369572637a5351bd193f07e059fa2
This commit is contained in:
drh
2000-07-28 14:32:48 +00:00
parent 78a3b40529
commit 2dfbbcafb8
19 changed files with 433 additions and 75 deletions

View File

@@ -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
View File

@@ -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.
#

View File

@@ -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.
#

View File

@@ -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

View File

@@ -1 +1 @@
937c27b7e18505d0f8b85d2040db8d6a8b7cd441
4fe8e51c248369572637a5351bd193f07e059fa2

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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++){

View File

@@ -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_ */

View File

@@ -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 */
};

View File

@@ -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;

View File

@@ -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
View 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

View File

@@ -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,

View File

@@ -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>
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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>