mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-06-24 00:21:05 +08:00
port complete SQLite-3.8.1 to RT-Thread
This commit is contained in:
Vendored
+26
@@ -2,3 +2,29 @@
|
||||
|
||||
## 简介
|
||||
初始版本基于SQLite 3.8.1版本,使用混合单文件结构源代码
|
||||
|
||||
测试方法:
|
||||
1.
|
||||
在rtconfig.h中定义一下宏
|
||||
/*
|
||||
* SQLite compile macro
|
||||
*/
|
||||
#define RT_USING_SQLITE
|
||||
#define SQLITE_OMIT_LOAD_EXTENSION 1
|
||||
#define SQLITE_RTT_NO_WIDE 1
|
||||
#define SQLITE_OMIT_WAL
|
||||
#define SQLITE_ENABLE_LOCKING_STYLE 0
|
||||
#define SQLITE_DISABLE_LOCKING_STYLE 1
|
||||
#define SQLITE_TEMP_STORE 1
|
||||
#define SQLITE_THREADSAFE 1
|
||||
#define HAVE_READLINE 0
|
||||
#define NDEBUG
|
||||
#define _HAVE_SQLITE_CONFIG_H
|
||||
#define BUILD_sqlite
|
||||
#define SQLITE_OS_OTHER 1
|
||||
#define SQLITE_OS_RTT 1
|
||||
2.
|
||||
在test目录下找一个测试样例来加入工程进行测试.
|
||||
推荐用mini2440bsp,因为板子的ram较大。
|
||||
|
||||
注意shell.c还没有移植的。请不要使用。
|
||||
|
||||
+3
-1
@@ -3,7 +3,9 @@ import os
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
src = Split("""
|
||||
sqlite3.c
|
||||
""")
|
||||
CPPPATH = [cwd, str(Dir('#'))]
|
||||
|
||||
group = DefineGroup('sqlite', src, depend = ['RT_USING_SQLITE'], CPPPATH = CPPPATH)
|
||||
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
/* config.h. Generated from config.h.in by configure. */
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
/* #undef HAVE_DLFCN_H */
|
||||
|
||||
/* Define to 1 if you have the `fdatasync' function. */
|
||||
/* #undef HAVE_FDATASYNC */
|
||||
|
||||
/* Define to 1 if you have the `gmtime_r' function. */
|
||||
#define HAVE_GMTIME_R 1
|
||||
|
||||
/* Define to 1 if the system has the type `int16_t'. */
|
||||
#define HAVE_INT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `int32_t'. */
|
||||
#define HAVE_INT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `int64_t'. */
|
||||
#define HAVE_INT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `int8_t'. */
|
||||
#define HAVE_INT8_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `intptr_t'. */
|
||||
#define HAVE_INTPTR_T 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `localtime_r' function. */
|
||||
#define HAVE_LOCALTIME_R 1
|
||||
|
||||
/* Define to 1 if you have the `localtime_s' function. */
|
||||
/* #undef HAVE_LOCALTIME_S */
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
/* #define HAVE_MALLOC_H 0 */
|
||||
|
||||
/* Define to 1 if you have the `malloc_usable_size' function. */
|
||||
/* #define HAVE_MALLOC_USABLE_SIZE 0 */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
/* #undef HAVE_MEMORY_H */
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
/* #undef HAVE_STRINGS_H */
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint16_t'. */
|
||||
#define HAVE_UINT16_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint32_t'. */
|
||||
#define HAVE_UINT32_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint64_t'. */
|
||||
#define HAVE_UINT64_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uint8_t'. */
|
||||
#define HAVE_UINT8_T 1
|
||||
|
||||
/* Define to 1 if the system has the type `uintptr_t'. */
|
||||
#define HAVE_UINTPTR_T 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to 1 if you have the `usleep' function. */
|
||||
/* #undef HAVE_USLEEP */
|
||||
|
||||
/* Define to 1 if you have the utime() library function. */
|
||||
/* #undef HAVE_UTIME */
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT ""
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "sqlite"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "sqlite 3.8.1"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "sqlite"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "3.8.1"
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
/* #undef _FILE_OFFSET_BITS */
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
/* #undef _LARGE_FILES */
|
||||
Vendored
+3421
-189
File diff suppressed because it is too large
Load Diff
+6549
File diff suppressed because it is too large
Load Diff
+664
File diff suppressed because it is too large
Load Diff
+628
File diff suppressed because it is too large
Load Diff
+722
File diff suppressed because it is too large
Load Diff
+217
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the utf.c module in SQLite. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library. Specifically, the code in this file
|
||||
** is used for testing the SQLite routines for converting between
|
||||
** the various supported unicode encodings.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** The first argument is a TCL UTF-8 string. Return the byte array
|
||||
** object with the encoded representation of the string, including
|
||||
** the NULL terminator.
|
||||
*/
|
||||
static int binarize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int len;
|
||||
char *bytes;
|
||||
Tcl_Obj *pRet;
|
||||
assert(objc==2);
|
||||
|
||||
bytes = Tcl_GetStringFromObj(objv[1], &len);
|
||||
pRet = Tcl_NewByteArrayObj((u8*)bytes, len+1);
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_value_overhead <repeat-count> <do-calls>.
|
||||
**
|
||||
** This routine is used to test the overhead of calls to
|
||||
** sqlite3_value_text(), on a value that contains a UTF-8 string. The idea
|
||||
** is to figure out whether or not it is a problem to use sqlite3_value
|
||||
** structures with collation sequence functions.
|
||||
**
|
||||
** If <do-calls> is 0, then the calls to sqlite3_value_text() are not
|
||||
** actually made.
|
||||
*/
|
||||
static int test_value_overhead(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int do_calls;
|
||||
int repeat_count;
|
||||
int i;
|
||||
Mem val;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " <repeat-count> <do-calls>", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &repeat_count) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &do_calls) ) return TCL_ERROR;
|
||||
|
||||
val.flags = MEM_Str|MEM_Term|MEM_Static;
|
||||
val.z = "hello world";
|
||||
val.type = SQLITE_TEXT;
|
||||
val.enc = SQLITE_UTF8;
|
||||
|
||||
for(i=0; i<repeat_count; i++){
|
||||
if( do_calls ){
|
||||
sqlite3_value_text(&val);
|
||||
}
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static u8 name_to_enc(Tcl_Interp *interp, Tcl_Obj *pObj){
|
||||
struct EncName {
|
||||
char *zName;
|
||||
u8 enc;
|
||||
} encnames[] = {
|
||||
{ "UTF8", SQLITE_UTF8 },
|
||||
{ "UTF16LE", SQLITE_UTF16LE },
|
||||
{ "UTF16BE", SQLITE_UTF16BE },
|
||||
{ "UTF16", SQLITE_UTF16 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
struct EncName *pEnc;
|
||||
char *z = Tcl_GetString(pObj);
|
||||
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
|
||||
if( 0==sqlite3StrICmp(z, pEnc->zName) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !pEnc->enc ){
|
||||
Tcl_AppendResult(interp, "No such encoding: ", z, 0);
|
||||
}
|
||||
if( pEnc->enc==SQLITE_UTF16 ){
|
||||
return SQLITE_UTF16NATIVE;
|
||||
}
|
||||
return pEnc->enc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: test_translate <string/blob> <from enc> <to enc> ?<transient>?
|
||||
**
|
||||
*/
|
||||
static int test_translate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
u8 enc_from;
|
||||
u8 enc_to;
|
||||
sqlite3_value *pVal;
|
||||
|
||||
char *z;
|
||||
int len;
|
||||
void (*xDel)(void *p) = SQLITE_STATIC;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0),
|
||||
" <string/blob> <from enc> <to enc>", 0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc==5 ){
|
||||
xDel = sqlite3_free;
|
||||
}
|
||||
|
||||
enc_from = name_to_enc(interp, objv[2]);
|
||||
if( !enc_from ) return TCL_ERROR;
|
||||
enc_to = name_to_enc(interp, objv[3]);
|
||||
if( !enc_to ) return TCL_ERROR;
|
||||
|
||||
pVal = sqlite3ValueNew(0);
|
||||
|
||||
if( enc_from==SQLITE_UTF8 ){
|
||||
z = Tcl_GetString(objv[1]);
|
||||
if( objc==5 ){
|
||||
z = sqlite3_mprintf("%s", z);
|
||||
}
|
||||
sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
|
||||
}else{
|
||||
z = (char*)Tcl_GetByteArrayFromObj(objv[1], &len);
|
||||
if( objc==5 ){
|
||||
char *zTmp = z;
|
||||
z = sqlite3_malloc(len);
|
||||
memcpy(z, zTmp, len);
|
||||
}
|
||||
sqlite3ValueSetStr(pVal, -1, z, enc_from, xDel);
|
||||
}
|
||||
|
||||
z = (char *)sqlite3ValueText(pVal, enc_to);
|
||||
len = sqlite3ValueBytes(pVal, enc_to) + (enc_to==SQLITE_UTF8?1:2);
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((u8*)z, len));
|
||||
|
||||
sqlite3ValueFree(pVal);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: translate_selftest
|
||||
**
|
||||
** Call sqlite3UtfSelfTest() to run the internal tests for unicode
|
||||
** translation. If there is a problem an assert() will fail.
|
||||
**/
|
||||
void sqlite3UtfSelfTest(void);
|
||||
static int test_translate_selftest(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
sqlite3UtfSelfTest();
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest5_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "binarize", (Tcl_ObjCmdProc*)binarize },
|
||||
{ "test_value_overhead", (Tcl_ObjCmdProc*)test_value_overhead },
|
||||
{ "test_translate", (Tcl_ObjCmdProc*)test_translate },
|
||||
{ "translate_selftest", (Tcl_ObjCmdProc*)test_translate_selftest},
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
+1020
File diff suppressed because it is too large
Load Diff
+714
File diff suppressed because it is too large
Load Diff
+1396
File diff suppressed because it is too large
Load Diff
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
** 2007 March 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 contains obscure tests of the C-interface required
|
||||
** for completeness. Test code is written in C for these cases
|
||||
** as there is not much point in binding to Tcl.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** c_collation_test
|
||||
*/
|
||||
static int c_collation_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
const char *zErrFunction = "N/A";
|
||||
sqlite3 *db;
|
||||
|
||||
int rc;
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Open a database. */
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
zErrFunction = "sqlite3_open";
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
rc = sqlite3_create_collation(db, "collate", 456, 0, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
sqlite3_close(db);
|
||||
zErrFunction = "sqlite3_create_collation";
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
return TCL_OK;
|
||||
|
||||
error_out:
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** c_realloc_test
|
||||
*/
|
||||
static int c_realloc_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
void *p;
|
||||
const char *zErrFunction = "N/A";
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
p = sqlite3_malloc(5);
|
||||
if( !p ){
|
||||
zErrFunction = "sqlite3_malloc";
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
/* Test that realloc()ing a block of memory to a negative size is
|
||||
** the same as free()ing that memory.
|
||||
*/
|
||||
p = sqlite3_realloc(p, -1);
|
||||
if( p ){
|
||||
zErrFunction = "sqlite3_realloc";
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
|
||||
error_out:
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** c_misuse_test
|
||||
*/
|
||||
static int c_misuse_test(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
const char *zErrFunction = "N/A";
|
||||
sqlite3 *db = 0;
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/* Open a database. Then close it again. We need to do this so that
|
||||
** we have a "closed database handle" to pass to various API functions.
|
||||
*/
|
||||
rc = sqlite3_open(":memory:", &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
zErrFunction = "sqlite3_open";
|
||||
goto error_out;
|
||||
}
|
||||
sqlite3_close(db);
|
||||
|
||||
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_errcode";
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 ); /* Verify that pStmt is zeroed even on a MISUSE error */
|
||||
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare_v2(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare_v2";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare16(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare16";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 );
|
||||
pStmt = (sqlite3_stmt*)1234;
|
||||
rc = sqlite3_prepare16_v2(db, 0, 0, &pStmt, 0);
|
||||
if( rc!=SQLITE_MISUSE ){
|
||||
zErrFunction = "sqlite3_prepare16_v2";
|
||||
goto error_out;
|
||||
}
|
||||
assert( pStmt==0 );
|
||||
#endif
|
||||
|
||||
return TCL_OK;
|
||||
|
||||
error_out:
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, "Error testing function: ", zErrFunction, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest9_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "c_misuse_test", c_misuse_test, 0 },
|
||||
{ "c_realloc_test", c_realloc_test, 0 },
|
||||
{ "c_collation_test", c_collation_test, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
** 2005 December 14
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains a binding of the asynchronous IO extension interface
|
||||
** (defined in ext/async/sqlite3async.h) to Tcl.
|
||||
*/
|
||||
|
||||
#define TCL_THREADS
|
||||
#include <tcl.h>
|
||||
|
||||
#ifdef SQLITE_ENABLE_ASYNCIO
|
||||
|
||||
#include "sqlite3async.h"
|
||||
#include "sqlite3.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* From main.c */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
|
||||
struct TestAsyncGlobal {
|
||||
int isInstalled; /* True when async VFS is installed */
|
||||
} testasync_g = { 0 };
|
||||
|
||||
TCL_DECLARE_MUTEX(testasync_g_writerMutex);
|
||||
|
||||
/*
|
||||
** sqlite3async_initialize PARENT-VFS ISDEFAULT
|
||||
*/
|
||||
static int testAsyncInit(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zParent;
|
||||
int isDefault;
|
||||
int rc;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PARENT-VFS ISDEFAULT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zParent = Tcl_GetString(objv[1]);
|
||||
if( !*zParent ) {
|
||||
zParent = 0;
|
||||
}
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3async_initialize(zParent, isDefault);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3async_shutdown
|
||||
*/
|
||||
static int testAsyncShutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3async_shutdown();
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static Tcl_ThreadCreateType tclWriterThread(ClientData pIsStarted){
|
||||
Tcl_MutexLock(&testasync_g_writerMutex);
|
||||
*((int *)pIsStarted) = 1;
|
||||
sqlite3async_run();
|
||||
Tcl_MutexUnlock(&testasync_g_writerMutex);
|
||||
Tcl_ExitThread(0);
|
||||
TCL_THREAD_CREATE_RETURN;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3async_start
|
||||
**
|
||||
** Start a new writer thread.
|
||||
*/
|
||||
static int testAsyncStart(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
volatile int isStarted = 0;
|
||||
ClientData threadData = (ClientData)&isStarted;
|
||||
|
||||
Tcl_ThreadId x;
|
||||
const int nStack = TCL_THREAD_STACK_DEFAULT;
|
||||
const int flags = TCL_THREAD_NOFLAGS;
|
||||
int rc;
|
||||
|
||||
rc = Tcl_CreateThread(&x, tclWriterThread, threadData, nStack, flags);
|
||||
if( rc!=TCL_OK ){
|
||||
Tcl_AppendResult(interp, "Tcl_CreateThread() failed", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
while( isStarted==0 ) { /* Busy loop */ }
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3async_wait
|
||||
**
|
||||
** Wait for the current writer thread to terminate.
|
||||
**
|
||||
** If the current writer thread is set to run forever then this
|
||||
** command would block forever. To prevent that, an error is returned.
|
||||
*/
|
||||
static int testAsyncWait(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int eCond;
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
sqlite3async_control(SQLITEASYNC_GET_HALT, &eCond);
|
||||
if( eCond==SQLITEASYNC_HALT_NEVER ){
|
||||
Tcl_AppendResult(interp, "would block forever", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_MutexLock(&testasync_g_writerMutex);
|
||||
Tcl_MutexUnlock(&testasync_g_writerMutex);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3async_control OPTION ?VALUE?
|
||||
*/
|
||||
static int testAsyncControl(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int aeOpt[] = { SQLITEASYNC_HALT, SQLITEASYNC_DELAY, SQLITEASYNC_LOCKFILES };
|
||||
const char *azOpt[] = { "halt", "delay", "lockfiles", 0 };
|
||||
const char *az[] = { "never", "now", "idle", 0 };
|
||||
int iVal;
|
||||
int eOpt;
|
||||
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "OPTION ?VALUE?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIndexFromObj(interp, objv[1], azOpt, "option", 0, &eOpt) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
eOpt = aeOpt[eOpt];
|
||||
|
||||
if( objc==3 ){
|
||||
switch( eOpt ){
|
||||
case SQLITEASYNC_HALT: {
|
||||
assert( SQLITEASYNC_HALT_NEVER==0 );
|
||||
assert( SQLITEASYNC_HALT_NOW==1 );
|
||||
assert( SQLITEASYNC_HALT_IDLE==2 );
|
||||
if( Tcl_GetIndexFromObj(interp, objv[2], az, "value", 0, &iVal) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITEASYNC_DELAY:
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case SQLITEASYNC_LOCKFILES:
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &iVal) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rc = sqlite3async_control(eOpt, iVal);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3async_control(
|
||||
eOpt==SQLITEASYNC_HALT ? SQLITEASYNC_GET_HALT :
|
||||
eOpt==SQLITEASYNC_DELAY ? SQLITEASYNC_GET_DELAY :
|
||||
SQLITEASYNC_GET_LOCKFILES, &iVal);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( eOpt==SQLITEASYNC_HALT ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(az[iVal], -1));
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal));
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_ENABLE_ASYNCIO */
|
||||
|
||||
/*
|
||||
** This routine registers the custom TCL commands defined in this
|
||||
** module. This should be the only procedure visible from outside
|
||||
** of this module.
|
||||
*/
|
||||
int Sqlitetestasync_Init(Tcl_Interp *interp){
|
||||
#ifdef SQLITE_ENABLE_ASYNCIO
|
||||
Tcl_CreateObjCommand(interp,"sqlite3async_start",testAsyncStart,0,0);
|
||||
Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
|
||||
|
||||
Tcl_CreateObjCommand(interp,"sqlite3async_control",testAsyncControl,0,0);
|
||||
Tcl_CreateObjCommand(interp,"sqlite3async_initialize",testAsyncInit,0,0);
|
||||
Tcl_CreateObjCommand(interp,"sqlite3async_shutdown",testAsyncShutdown,0,0);
|
||||
#endif /* SQLITE_ENABLE_ASYNCIO */
|
||||
return TCL_OK;
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
** 2006 August 23
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Test extension for testing the sqlite3_auto_extension() function.
|
||||
*/
|
||||
#include "tcl.h"
|
||||
#include "sqlite3ext.h"
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** The sqr() SQL function returns the square of its input value.
|
||||
*/
|
||||
static void sqrFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
double r = sqlite3_value_double(argv[0]);
|
||||
sqlite3_result_double(context, r*r);
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the entry point to register the extension for the sqr() function.
|
||||
*/
|
||||
static int sqr_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
sqlite3_create_function(db, "sqr", 1, SQLITE_ANY, 0, sqrFunc, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** The cube() SQL function returns the cube of its input value.
|
||||
*/
|
||||
static void cubeFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
double r = sqlite3_value_double(argv[0]);
|
||||
sqlite3_result_double(context, r*r*r);
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the entry point to register the extension for the cube() function.
|
||||
*/
|
||||
static int cube_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
sqlite3_create_function(db, "cube", 1, SQLITE_ANY, 0, cubeFunc, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a broken extension entry point
|
||||
*/
|
||||
static int broken_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
char *zErr;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
zErr = sqlite3_mprintf("broken autoext!");
|
||||
*pzErrMsg = zErr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_auto_extension_sqr
|
||||
**
|
||||
** Register the "sqr" extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtSqrObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)sqr_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_cancel_auto_extension_sqr
|
||||
**
|
||||
** Unregister the "sqr" extension.
|
||||
*/
|
||||
static int cancelAutoExtSqrObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)sqr_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_auto_extension_cube
|
||||
**
|
||||
** Register the "cube" extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtCubeObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)cube_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_cancel_auto_extension_cube
|
||||
**
|
||||
** Unregister the "cube" extension.
|
||||
*/
|
||||
static int cancelAutoExtCubeObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)cube_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_auto_extension_broken
|
||||
**
|
||||
** Register the broken extension to be loaded automatically.
|
||||
*/
|
||||
static int autoExtBrokenObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_auto_extension((void*)broken_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_cancel_auto_extension_broken
|
||||
**
|
||||
** Unregister the broken extension.
|
||||
*/
|
||||
static int cancelAutoExtBrokenObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = sqlite3_cancel_auto_extension((void*)broken_init);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_reset_auto_extension
|
||||
**
|
||||
** Reset all auto-extensions
|
||||
*/
|
||||
static int resetAutoExtObjCmd(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_reset_auto_extension();
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This procedure registers the TCL procs defined in this file.
|
||||
*/
|
||||
int Sqlitetest_autoext_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_sqr",
|
||||
autoExtSqrObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_cube",
|
||||
autoExtCubeObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_broken",
|
||||
autoExtBrokenObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_sqr",
|
||||
cancelAutoExtSqrObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_cube",
|
||||
cancelAutoExtCubeObjCmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_broken",
|
||||
cancelAutoExtBrokenObjCmd, 0, 0);
|
||||
#endif
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_reset_auto_extension",
|
||||
resetAutoExtObjCmd, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
** 2009 January 28
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains test logic for the sqlite3_backup() interface.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
#include <sqlite3.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* These functions are implemented in main.c. */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/* These functions are implemented in test1.c. */
|
||||
extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
|
||||
|
||||
static int backupTestCmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *const*objv
|
||||
){
|
||||
enum BackupSubCommandEnum {
|
||||
BACKUP_STEP, BACKUP_FINISH, BACKUP_REMAINING, BACKUP_PAGECOUNT
|
||||
};
|
||||
struct BackupSubCommand {
|
||||
const char *zCmd;
|
||||
enum BackupSubCommandEnum eCmd;
|
||||
int nArg;
|
||||
const char *zArg;
|
||||
} aSub[] = {
|
||||
{"step", BACKUP_STEP , 1, "npage" },
|
||||
{"finish", BACKUP_FINISH , 0, "" },
|
||||
{"remaining", BACKUP_REMAINING , 0, "" },
|
||||
{"pagecount", BACKUP_PAGECOUNT , 0, "" },
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
sqlite3_backup *p = (sqlite3_backup *)clientData;
|
||||
int iCmd;
|
||||
int rc;
|
||||
|
||||
rc = Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[1], aSub, sizeof(aSub[0]), "option", 0, &iCmd
|
||||
);
|
||||
if( rc!=TCL_OK ){
|
||||
return rc;
|
||||
}
|
||||
if( objc!=(2 + aSub[iCmd].nArg) ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, aSub[iCmd].zArg);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( aSub[iCmd].eCmd ){
|
||||
|
||||
case BACKUP_FINISH: {
|
||||
const char *zCmdName;
|
||||
Tcl_CmdInfo cmdInfo;
|
||||
zCmdName = Tcl_GetString(objv[0]);
|
||||
Tcl_GetCommandInfo(interp, zCmdName, &cmdInfo);
|
||||
cmdInfo.deleteProc = 0;
|
||||
Tcl_SetCommandInfo(interp, zCmdName, &cmdInfo);
|
||||
Tcl_DeleteCommand(interp, zCmdName);
|
||||
|
||||
rc = sqlite3_backup_finish(p);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
break;
|
||||
}
|
||||
|
||||
case BACKUP_STEP: {
|
||||
int nPage;
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &nPage) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_backup_step(p, nPage);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
break;
|
||||
}
|
||||
|
||||
case BACKUP_REMAINING:
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_backup_remaining(p)));
|
||||
break;
|
||||
|
||||
case BACKUP_PAGECOUNT:
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_backup_pagecount(p)));
|
||||
break;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void backupTestFinish(ClientData clientData){
|
||||
sqlite3_backup *pBackup = (sqlite3_backup *)clientData;
|
||||
sqlite3_backup_finish(pBackup);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_backup CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME
|
||||
**
|
||||
*/
|
||||
static int backupTestInit(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *const*objv
|
||||
){
|
||||
sqlite3_backup *pBackup;
|
||||
sqlite3 *pDestDb;
|
||||
sqlite3 *pSrcDb;
|
||||
const char *zDestName;
|
||||
const char *zSrcName;
|
||||
const char *zCmd;
|
||||
|
||||
if( objc!=6 ){
|
||||
Tcl_WrongNumArgs(
|
||||
interp, 1, objv, "CMDNAME DESTHANDLE DESTNAME SRCHANDLE SRCNAME"
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zCmd = Tcl_GetString(objv[1]);
|
||||
getDbPointer(interp, Tcl_GetString(objv[2]), &pDestDb);
|
||||
zDestName = Tcl_GetString(objv[3]);
|
||||
getDbPointer(interp, Tcl_GetString(objv[4]), &pSrcDb);
|
||||
zSrcName = Tcl_GetString(objv[5]);
|
||||
|
||||
pBackup = sqlite3_backup_init(pDestDb, zDestName, pSrcDb, zSrcName);
|
||||
if( !pBackup ){
|
||||
Tcl_AppendResult(interp, "sqlite3_backup_init() failed", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_CreateObjCommand(interp, zCmd, backupTestCmd, pBackup, backupTestFinish);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestbackup_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3_backup", backupTestInit, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
** 2007 May 05
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the btree.c module in SQLite. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
#include "btreeInt.h"
|
||||
#include <tcl.h>
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_shared_cache_report
|
||||
**
|
||||
** Return a list of file that are shared and the number of
|
||||
** references to each file.
|
||||
*/
|
||||
int sqlite3BtreeSharedCacheReport(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
extern BtShared *sqlite3SharedCacheList;
|
||||
BtShared *pBt;
|
||||
Tcl_Obj *pRet = Tcl_NewObj();
|
||||
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
|
||||
const char *zFile = sqlite3PagerFilename(pBt->pPager, 1);
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef));
|
||||
}
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print debugging information about all cursors to standard output.
|
||||
*/
|
||||
void sqlite3BtreeCursorList(Btree *p){
|
||||
#ifdef SQLITE_DEBUG
|
||||
BtCursor *pCur;
|
||||
BtShared *pBt = p->pBt;
|
||||
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
|
||||
MemPage *pPage = pCur->apPage[pCur->iPage];
|
||||
char *zMode = pCur->wrFlag ? "rw" : "ro";
|
||||
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
|
||||
pCur, pCur->pgnoRoot, zMode,
|
||||
pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage],
|
||||
(pCur->eState==CURSOR_VALID) ? "" : " eof"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+656
File diff suppressed because it is too large
Load Diff
+679
File diff suppressed because it is too large
Load Diff
+398
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
** 2008 Jan 22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code that modified the OS layer in order to simulate
|
||||
** different device types (by overriding the return values of the
|
||||
** xDeviceCharacteristics() and xSectorSize() methods).
|
||||
*/
|
||||
#if SQLITE_TEST /* This file is used for testing only */
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Maximum pathname length supported by the devsym backend.
|
||||
*/
|
||||
#define DEVSYM_MAX_PATHNAME 512
|
||||
|
||||
/*
|
||||
** Name used to identify this VFS.
|
||||
*/
|
||||
#define DEVSYM_VFS_NAME "devsym"
|
||||
|
||||
typedef struct devsym_file devsym_file;
|
||||
struct devsym_file {
|
||||
sqlite3_file base;
|
||||
sqlite3_file *pReal;
|
||||
};
|
||||
|
||||
/*
|
||||
** Method declarations for devsym_file.
|
||||
*/
|
||||
static int devsymClose(sqlite3_file*);
|
||||
static int devsymRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int devsymWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int devsymTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int devsymSync(sqlite3_file*, int flags);
|
||||
static int devsymFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int devsymLock(sqlite3_file*, int);
|
||||
static int devsymUnlock(sqlite3_file*, int);
|
||||
static int devsymCheckReservedLock(sqlite3_file*, int *);
|
||||
static int devsymFileControl(sqlite3_file*, int op, void *pArg);
|
||||
static int devsymSectorSize(sqlite3_file*);
|
||||
static int devsymDeviceCharacteristics(sqlite3_file*);
|
||||
static int devsymShmLock(sqlite3_file*,int,int,int);
|
||||
static int devsymShmMap(sqlite3_file*,int,int,int, void volatile **);
|
||||
static void devsymShmBarrier(sqlite3_file*);
|
||||
static int devsymShmUnmap(sqlite3_file*,int);
|
||||
|
||||
/*
|
||||
** Method declarations for devsym_vfs.
|
||||
*/
|
||||
static int devsymOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
static int devsymDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
static int devsymAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int devsymFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
static void *devsymDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void devsymDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*devsymDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
|
||||
static void devsymDlClose(sqlite3_vfs*, void*);
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int devsymSleep(sqlite3_vfs*, int microseconds);
|
||||
static int devsymCurrentTime(sqlite3_vfs*, double*);
|
||||
|
||||
static sqlite3_vfs devsym_vfs = {
|
||||
2, /* iVersion */
|
||||
sizeof(devsym_file), /* szOsFile */
|
||||
DEVSYM_MAX_PATHNAME, /* mxPathname */
|
||||
0, /* pNext */
|
||||
DEVSYM_VFS_NAME, /* zName */
|
||||
0, /* pAppData */
|
||||
devsymOpen, /* xOpen */
|
||||
devsymDelete, /* xDelete */
|
||||
devsymAccess, /* xAccess */
|
||||
devsymFullPathname, /* xFullPathname */
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
devsymDlOpen, /* xDlOpen */
|
||||
devsymDlError, /* xDlError */
|
||||
devsymDlSym, /* xDlSym */
|
||||
devsymDlClose, /* xDlClose */
|
||||
#else
|
||||
0, /* xDlOpen */
|
||||
0, /* xDlError */
|
||||
0, /* xDlSym */
|
||||
0, /* xDlClose */
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
devsymRandomness, /* xRandomness */
|
||||
devsymSleep, /* xSleep */
|
||||
devsymCurrentTime, /* xCurrentTime */
|
||||
0, /* xGetLastError */
|
||||
0 /* xCurrentTimeInt64 */
|
||||
};
|
||||
|
||||
static sqlite3_io_methods devsym_io_methods = {
|
||||
2, /* iVersion */
|
||||
devsymClose, /* xClose */
|
||||
devsymRead, /* xRead */
|
||||
devsymWrite, /* xWrite */
|
||||
devsymTruncate, /* xTruncate */
|
||||
devsymSync, /* xSync */
|
||||
devsymFileSize, /* xFileSize */
|
||||
devsymLock, /* xLock */
|
||||
devsymUnlock, /* xUnlock */
|
||||
devsymCheckReservedLock, /* xCheckReservedLock */
|
||||
devsymFileControl, /* xFileControl */
|
||||
devsymSectorSize, /* xSectorSize */
|
||||
devsymDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
devsymShmMap, /* xShmMap */
|
||||
devsymShmLock, /* xShmLock */
|
||||
devsymShmBarrier, /* xShmBarrier */
|
||||
devsymShmUnmap /* xShmUnmap */
|
||||
};
|
||||
|
||||
struct DevsymGlobal {
|
||||
sqlite3_vfs *pVfs;
|
||||
int iDeviceChar;
|
||||
int iSectorSize;
|
||||
};
|
||||
struct DevsymGlobal g = {0, 0, 512};
|
||||
|
||||
/*
|
||||
** Close an devsym-file.
|
||||
*/
|
||||
static int devsymClose(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsClose(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from an devsym-file.
|
||||
*/
|
||||
static int devsymRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an devsym-file.
|
||||
*/
|
||||
static int devsymWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an devsym-file.
|
||||
*/
|
||||
static int devsymTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsTruncate(p->pReal, size);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync an devsym-file.
|
||||
*/
|
||||
static int devsymSync(sqlite3_file *pFile, int flags){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsSync(p->pReal, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of an devsym-file.
|
||||
*/
|
||||
static int devsymFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsFileSize(p->pReal, pSize);
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock an devsym-file.
|
||||
*/
|
||||
static int devsymLock(sqlite3_file *pFile, int eLock){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsLock(p->pReal, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock an devsym-file.
|
||||
*/
|
||||
static int devsymUnlock(sqlite3_file *pFile, int eLock){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsUnlock(p->pReal, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on an devsym-file.
|
||||
*/
|
||||
static int devsymCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsCheckReservedLock(p->pReal, pResOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on an devsym-file.
|
||||
*/
|
||||
static int devsymFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsFileControl(p->pReal, op, pArg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an devsym-file.
|
||||
*/
|
||||
static int devsymSectorSize(sqlite3_file *pFile){
|
||||
return g.iSectorSize;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an devsym-file.
|
||||
*/
|
||||
static int devsymDeviceCharacteristics(sqlite3_file *pFile){
|
||||
return g.iDeviceChar;
|
||||
}
|
||||
|
||||
/*
|
||||
** Shared-memory methods are all pass-thrus.
|
||||
*/
|
||||
static int devsymShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmLock(p->pReal, ofst, n, flags);
|
||||
}
|
||||
static int devsymShmMap(
|
||||
sqlite3_file *pFile,
|
||||
int iRegion,
|
||||
int szRegion,
|
||||
int isWrite,
|
||||
void volatile **pp
|
||||
){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
|
||||
}
|
||||
static void devsymShmBarrier(sqlite3_file *pFile){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
sqlite3OsShmBarrier(p->pReal);
|
||||
}
|
||||
static int devsymShmUnmap(sqlite3_file *pFile, int delFlag){
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
return sqlite3OsShmUnmap(p->pReal, delFlag);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Open an devsym file handle.
|
||||
*/
|
||||
static int devsymOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
int rc;
|
||||
devsym_file *p = (devsym_file *)pFile;
|
||||
p->pReal = (sqlite3_file *)&p[1];
|
||||
rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
|
||||
if( p->pReal->pMethods ){
|
||||
pFile->pMethods = &devsym_io_methods;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete the file located at zPath. If the dirSync argument is true,
|
||||
** ensure the file-system modifications are synced to disk before
|
||||
** returning.
|
||||
*/
|
||||
static int devsymDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return sqlite3OsDelete(g.pVfs, zPath, dirSync);
|
||||
}
|
||||
|
||||
/*
|
||||
** Test for access permissions. Return true if the requested permission
|
||||
** is available, or false otherwise.
|
||||
*/
|
||||
static int devsymAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate buffer zOut with the full canonical pathname corresponding
|
||||
** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
||||
** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
|
||||
*/
|
||||
static int devsymFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
/*
|
||||
** Open the dynamic library located at zPath and return a handle.
|
||||
*/
|
||||
static void *devsymDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return sqlite3OsDlOpen(g.pVfs, zPath);
|
||||
}
|
||||
|
||||
/*
|
||||
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
||||
** utf-8 string describing the most recent error encountered associated
|
||||
** with dynamic libraries.
|
||||
*/
|
||||
static void devsymDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
sqlite3OsDlError(g.pVfs, nByte, zErrMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
||||
*/
|
||||
static void (*devsymDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return sqlite3OsDlSym(g.pVfs, p, zSym);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the dynamic library handle pHandle.
|
||||
*/
|
||||
static void devsymDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
sqlite3OsDlClose(g.pVfs, pHandle);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
|
||||
|
||||
/*
|
||||
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
||||
** random data.
|
||||
*/
|
||||
static int devsymRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for nMicro microseconds. Return the number of microseconds
|
||||
** actually slept.
|
||||
*/
|
||||
static int devsymSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return sqlite3OsSleep(g.pVfs, nMicro);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time as a Julian Day number in *pTimeOut.
|
||||
*/
|
||||
static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return g.pVfs->xCurrentTime(g.pVfs, pTimeOut);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This procedure registers the devsym vfs with SQLite. If the argument is
|
||||
** true, the devsym vfs becomes the new default vfs. It is the only publicly
|
||||
** available function in this file.
|
||||
*/
|
||||
void devsym_register(int iDeviceChar, int iSectorSize){
|
||||
if( g.pVfs==0 ){
|
||||
g.pVfs = sqlite3_vfs_find(0);
|
||||
devsym_vfs.szOsFile += g.pVfs->szOsFile;
|
||||
sqlite3_vfs_register(&devsym_vfs, 0);
|
||||
}
|
||||
if( iDeviceChar>=0 ){
|
||||
g.iDeviceChar = iDeviceChar;
|
||||
}else{
|
||||
g.iDeviceChar = 0;
|
||||
}
|
||||
if( iSectorSize>=0 ){
|
||||
g.iSectorSize = iSectorSize;
|
||||
}else{
|
||||
g.iSectorSize = 512;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
** 2013 Jan 11
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table interfaces. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** The FS virtual table is created as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE tbl USING fs(idx);
|
||||
**
|
||||
** where idx is the name of a table in the db with 2 columns. The virtual
|
||||
** table also has two columns - file path and file contents.
|
||||
**
|
||||
** The first column of table idx must be an IPK, and the second contains file
|
||||
** paths. For example:
|
||||
**
|
||||
** CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT);
|
||||
** INSERT INTO idx VALUES(4, '/etc/passwd');
|
||||
**
|
||||
** Adding the row to the idx table automatically creates a row in the
|
||||
** virtual table with rowid=4, path=/etc/passwd and a text field that
|
||||
** contains data read from file /etc/passwd on disk.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct fs_vtab fs_vtab;
|
||||
typedef struct fs_cursor fs_cursor;
|
||||
|
||||
/*
|
||||
** A fs virtual-table object
|
||||
*/
|
||||
struct fs_vtab {
|
||||
sqlite3_vtab base;
|
||||
sqlite3 *db;
|
||||
char *zDb; /* Name of db containing zTbl */
|
||||
char *zTbl; /* Name of docid->file map table */
|
||||
};
|
||||
|
||||
/* A fs cursor object */
|
||||
struct fs_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pStmt;
|
||||
char *zBuf;
|
||||
int nBuf;
|
||||
int nAlloc;
|
||||
};
|
||||
|
||||
/*
|
||||
** This function is the implementation of both the xConnect and xCreate
|
||||
** methods of the fs virtual table.
|
||||
**
|
||||
** The argv[] array contains the following:
|
||||
**
|
||||
** argv[0] -> module name ("fs")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[...] -> other module argument fields.
|
||||
*/
|
||||
static int fsConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
fs_vtab *pVtab;
|
||||
int nByte;
|
||||
const char *zTbl;
|
||||
const char *zDb = argv[1];
|
||||
|
||||
if( argc!=4 ){
|
||||
*pzErr = sqlite3_mprintf("wrong number of arguments");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
zTbl = argv[3];
|
||||
|
||||
nByte = sizeof(fs_vtab) + (int)strlen(zTbl) + 1 + (int)strlen(zDb) + 1;
|
||||
pVtab = (fs_vtab *)sqlite3MallocZero( nByte );
|
||||
if( !pVtab ) return SQLITE_NOMEM;
|
||||
|
||||
pVtab->zTbl = (char *)&pVtab[1];
|
||||
pVtab->zDb = &pVtab->zTbl[strlen(zTbl)+1];
|
||||
pVtab->db = db;
|
||||
memcpy(pVtab->zTbl, zTbl, strlen(zTbl));
|
||||
memcpy(pVtab->zDb, zDb, strlen(zDb));
|
||||
*ppVtab = &pVtab->base;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE xyz(path TEXT, data TEXT)");
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* Note that for this virtual table, the xCreate and xConnect
|
||||
** methods are identical. */
|
||||
|
||||
static int fsDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
|
||||
/*
|
||||
** Open a new fs cursor.
|
||||
*/
|
||||
static int fsOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
fs_cursor *pCur;
|
||||
pCur = sqlite3MallocZero(sizeof(fs_cursor));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a fs cursor.
|
||||
*/
|
||||
static int fsClose(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor *)cur;
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
sqlite3_free(pCur->zBuf);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsNext(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor *)cur;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_step(pCur->pStmt);
|
||||
if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
fs_cursor *pCur = (fs_cursor *)pVtabCursor;
|
||||
fs_vtab *p = (fs_vtab *)(pVtabCursor->pVtab);
|
||||
|
||||
assert( (idxNum==0 && argc==0) || (idxNum==1 && argc==1) );
|
||||
if( idxNum==1 ){
|
||||
char *zStmt = sqlite3_mprintf(
|
||||
"SELECT * FROM %Q.%Q WHERE rowid=?", p->zDb, p->zTbl);
|
||||
if( !zStmt ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_value(pCur->pStmt, 1, argv[0]);
|
||||
}
|
||||
}else{
|
||||
char *zStmt = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zTbl);
|
||||
if( !zStmt ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zStmt);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsNext(pVtabCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
|
||||
assert( i==0 || i==1 );
|
||||
if( i==0 ){
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0));
|
||||
}else{
|
||||
const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
|
||||
struct stat sbuf;
|
||||
int fd;
|
||||
int n;
|
||||
|
||||
fd = open(zFile, O_RDONLY);
|
||||
if( fd<0 ) return SQLITE_IOERR;
|
||||
fstat(fd, &sbuf);
|
||||
|
||||
if( sbuf.st_size>=pCur->nAlloc ){
|
||||
int nNew = sbuf.st_size*2;
|
||||
char *zNew;
|
||||
if( nNew<1024 ) nNew = 1024;
|
||||
|
||||
zNew = sqlite3Realloc(pCur->zBuf, nNew);
|
||||
if( zNew==0 ){
|
||||
close(fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCur->zBuf = zNew;
|
||||
pCur->nAlloc = nNew;
|
||||
}
|
||||
|
||||
n = (int)read(fd, pCur->zBuf, sbuf.st_size);
|
||||
close(fd);
|
||||
if( n!=sbuf.st_size ) return SQLITE_ERROR;
|
||||
pCur->nBuf = sbuf.st_size;
|
||||
pCur->zBuf[pCur->nBuf] = '\0';
|
||||
|
||||
sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
*pRowid = sqlite3_column_int64(pCur->pStmt, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fsEof(sqlite3_vtab_cursor *cur){
|
||||
fs_cursor *pCur = (fs_cursor*)cur;
|
||||
return (sqlite3_data_count(pCur->pStmt)==0);
|
||||
}
|
||||
|
||||
static int fsBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int ii;
|
||||
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
if( pCons->iColumn<0 && pCons->usable
|
||||
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
struct sqlite3_index_constraint_usage *pUsage;
|
||||
pUsage = &pIdxInfo->aConstraintUsage[ii];
|
||||
pUsage->omit = 0;
|
||||
pUsage->argvIndex = 1;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 1.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module fsModule = {
|
||||
0, /* iVersion */
|
||||
fsConnect,
|
||||
fsConnect,
|
||||
fsBestIndex,
|
||||
fsDisconnect,
|
||||
fsDisconnect,
|
||||
fsOpen, /* xOpen - open a cursor */
|
||||
fsClose, /* xClose - close a cursor */
|
||||
fsFilter, /* xFilter - configure scan constraints */
|
||||
fsNext, /* xNext - advance a cursor */
|
||||
fsEof, /* xEof - check for end of scan */
|
||||
fsColumn, /* xColumn - read data */
|
||||
fsRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_fs_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "fs", &fsModule, (void *)interp);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestfs_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_fs_module", register_fs_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
+767
File diff suppressed because it is too large
Load Diff
+388
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
** 2007 April 6
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** implements TCL commands for reading and writing the binary
|
||||
** database files and displaying the content of those files as
|
||||
** hexadecimal. We could, in theory, use the built-in "binary"
|
||||
** command of TCL to do a lot of this, but there are some issues
|
||||
** with historical versions of the "binary" command. So it seems
|
||||
** easier and safer to build our own mechanism.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/*
|
||||
** Convert binary to hex. The input zBuf[] contains N bytes of
|
||||
** binary data. zBuf[] is 2*n+1 bytes long. Overwrite zBuf[]
|
||||
** with a hexadecimal representation of its original binary input.
|
||||
*/
|
||||
void sqlite3TestBinToHex(unsigned char *zBuf, int N){
|
||||
const unsigned char zHex[] = "0123456789ABCDEF";
|
||||
int i, j;
|
||||
unsigned char c;
|
||||
i = N*2;
|
||||
zBuf[i--] = 0;
|
||||
for(j=N-1; j>=0; j--){
|
||||
c = zBuf[j];
|
||||
zBuf[i--] = zHex[c&0xf];
|
||||
zBuf[i--] = zHex[c>>4];
|
||||
}
|
||||
assert( i==-1 );
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert hex to binary. The input zIn[] contains N bytes of
|
||||
** hexadecimal. Convert this into binary and write aOut[] with
|
||||
** the binary data. Spaces in the original input are ignored.
|
||||
** Return the number of bytes of binary rendered.
|
||||
*/
|
||||
int sqlite3TestHexToBin(const unsigned char *zIn, int N, unsigned char *aOut){
|
||||
const unsigned char aMap[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9,10, 0, 0, 0, 0, 0, 0,
|
||||
0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,11,12,13,14,15,16, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
int i, j;
|
||||
int hi=1;
|
||||
unsigned char c;
|
||||
|
||||
for(i=j=0; i<N; i++){
|
||||
c = aMap[zIn[i]];
|
||||
if( c==0 ) continue;
|
||||
if( hi ){
|
||||
aOut[j] = (c-1)<<4;
|
||||
hi = 0;
|
||||
}else{
|
||||
aOut[j++] |= c-1;
|
||||
hi = 1;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: hexio_read FILENAME OFFSET AMT
|
||||
**
|
||||
** Read AMT bytes from file FILENAME beginning at OFFSET from the
|
||||
** beginning of the file. Convert that information to hexadecimal
|
||||
** and return the resulting HEX string.
|
||||
*/
|
||||
static int hexio_read(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int offset;
|
||||
int amt, got;
|
||||
const char *zFile;
|
||||
unsigned char *zBuf;
|
||||
FILE *in;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET AMT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &amt) ) return TCL_ERROR;
|
||||
zFile = Tcl_GetString(objv[1]);
|
||||
zBuf = sqlite3_malloc( amt*2+1 );
|
||||
if( zBuf==0 ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
in = fopen(zFile, "rb");
|
||||
if( in==0 ){
|
||||
in = fopen(zFile, "r");
|
||||
}
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp, "cannot open input file ", zFile, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(in, offset, SEEK_SET);
|
||||
got = (int)fread(zBuf, 1, amt, in);
|
||||
fclose(in);
|
||||
if( got<0 ){
|
||||
got = 0;
|
||||
}
|
||||
sqlite3TestBinToHex(zBuf, got);
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
sqlite3_free(zBuf);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: hexio_write FILENAME OFFSET DATA
|
||||
**
|
||||
** Write DATA into file FILENAME beginning at OFFSET from the
|
||||
** beginning of the file. DATA is expressed in hexadecimal.
|
||||
*/
|
||||
static int hexio_write(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int offset;
|
||||
int nIn, nOut, written;
|
||||
const char *zFile;
|
||||
const unsigned char *zIn;
|
||||
unsigned char *aOut;
|
||||
FILE *out;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME OFFSET HEXDATA");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &offset) ) return TCL_ERROR;
|
||||
zFile = Tcl_GetString(objv[1]);
|
||||
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[3], &nIn);
|
||||
aOut = sqlite3_malloc( nIn/2 );
|
||||
if( aOut==0 ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
|
||||
out = fopen(zFile, "r+b");
|
||||
if( out==0 ){
|
||||
out = fopen(zFile, "r+");
|
||||
}
|
||||
if( out==0 ){
|
||||
Tcl_AppendResult(interp, "cannot open output file ", zFile, 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(out, offset, SEEK_SET);
|
||||
written = (int)fwrite(aOut, 1, nOut, out);
|
||||
sqlite3_free(aOut);
|
||||
fclose(out);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(written));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** USAGE: hexio_get_int HEXDATA
|
||||
**
|
||||
** Interpret the HEXDATA argument as a big-endian integer. Return
|
||||
** the value of that integer. HEXDATA can contain between 2 and 8
|
||||
** hexadecimal digits.
|
||||
*/
|
||||
static int hexio_get_int(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int val;
|
||||
int nIn, nOut;
|
||||
const unsigned char *zIn;
|
||||
unsigned char *aOut;
|
||||
unsigned char aNum[4];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HEXDATA");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zIn = (const unsigned char *)Tcl_GetStringFromObj(objv[1], &nIn);
|
||||
aOut = sqlite3_malloc( nIn/2 );
|
||||
if( aOut==0 ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
nOut = sqlite3TestHexToBin(zIn, nIn, aOut);
|
||||
if( nOut>=4 ){
|
||||
memcpy(aNum, aOut, 4);
|
||||
}else{
|
||||
memset(aNum, 0, sizeof(aNum));
|
||||
memcpy(&aNum[4-nOut], aOut, nOut);
|
||||
}
|
||||
sqlite3_free(aOut);
|
||||
val = (aNum[0]<<24) | (aNum[1]<<16) | (aNum[2]<<8) | aNum[3];
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(val));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** USAGE: hexio_render_int16 INTEGER
|
||||
**
|
||||
** Render INTEGER has a 16-bit big-endian integer in hexadecimal.
|
||||
*/
|
||||
static int hexio_render_int16(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int val;
|
||||
unsigned char aNum[10];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
|
||||
aNum[0] = val>>8;
|
||||
aNum[1] = val;
|
||||
sqlite3TestBinToHex(aNum, 2);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 4));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** USAGE: hexio_render_int32 INTEGER
|
||||
**
|
||||
** Render INTEGER has a 32-bit big-endian integer in hexadecimal.
|
||||
*/
|
||||
static int hexio_render_int32(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int val;
|
||||
unsigned char aNum[10];
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &val) ) return TCL_ERROR;
|
||||
aNum[0] = val>>24;
|
||||
aNum[1] = val>>16;
|
||||
aNum[2] = val>>8;
|
||||
aNum[3] = val;
|
||||
sqlite3TestBinToHex(aNum, 4);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj((char*)aNum, 8));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** USAGE: utf8_to_utf8 HEX
|
||||
**
|
||||
** The argument is a UTF8 string represented in hexadecimal.
|
||||
** The UTF8 might not be well-formed. Run this string through
|
||||
** sqlite3Utf8to8() convert it back to hex and return the result.
|
||||
*/
|
||||
static int utf8_to_utf8(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifdef SQLITE_DEBUG
|
||||
int n;
|
||||
int nOut;
|
||||
const unsigned char *zOrig;
|
||||
unsigned char *z;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HEX");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zOrig = (unsigned char *)Tcl_GetStringFromObj(objv[1], &n);
|
||||
z = sqlite3_malloc( n+3 );
|
||||
n = sqlite3TestHexToBin(zOrig, n, z);
|
||||
z[n] = 0;
|
||||
nOut = sqlite3Utf8To8(z);
|
||||
sqlite3TestBinToHex(z,nOut);
|
||||
Tcl_AppendResult(interp, (char*)z, 0);
|
||||
sqlite3_free(z);
|
||||
return TCL_OK;
|
||||
#else
|
||||
Tcl_AppendResult(interp,
|
||||
"[utf8_to_utf8] unavailable - SQLITE_DEBUG not defined", 0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int getFts3Varint(const char *p, sqlite_int64 *v){
|
||||
const unsigned char *q = (const unsigned char *) p;
|
||||
sqlite_uint64 x = 0, y = 1;
|
||||
while( (*q & 0x80) == 0x80 ){
|
||||
x += y * (*q++ & 0x7f);
|
||||
y <<= 7;
|
||||
}
|
||||
x += y * (*q++);
|
||||
*v = (sqlite_int64) x;
|
||||
return (int) (q - (unsigned char *)p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** USAGE: read_fts3varint BLOB VARNAME
|
||||
**
|
||||
** Read a varint from the start of BLOB. Set variable VARNAME to contain
|
||||
** the interpreted value. Return the number of bytes of BLOB consumed.
|
||||
*/
|
||||
static int read_fts3varint(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int nBlob;
|
||||
unsigned char *zBlob;
|
||||
sqlite3_int64 iVal;
|
||||
int nVal;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BLOB VARNAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zBlob = Tcl_GetByteArrayFromObj(objv[1], &nBlob);
|
||||
|
||||
nVal = getFts3Varint((char*)zBlob, (sqlite3_int64 *)(&iVal));
|
||||
Tcl_ObjSetVar2(interp, objv[2], 0, Tcl_NewWideIntObj(iVal), 0);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(nVal));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetest_hexio_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aObjCmd[] = {
|
||||
{ "hexio_read", hexio_read },
|
||||
{ "hexio_write", hexio_write },
|
||||
{ "hexio_get_int", hexio_get_int },
|
||||
{ "hexio_render_int16", hexio_render_int16 },
|
||||
{ "hexio_render_int32", hexio_render_int32 },
|
||||
{ "utf8_to_utf8", utf8_to_utf8 },
|
||||
{ "read_fts3varint", read_fts3varint },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
** 2009 August 17
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** The code in this file is used for testing SQLite. It is not part of
|
||||
** the source code used in production systems.
|
||||
**
|
||||
** Specifically, this file tests the effect of errors while initializing
|
||||
** the various pluggable sub-systems from within sqlite3_initialize().
|
||||
** If an error occurs in sqlite3_initialize() the following should be
|
||||
** true:
|
||||
**
|
||||
** 1) An error code is returned to the user, and
|
||||
** 2) A subsequent call to sqlite3_shutdown() calls the shutdown method
|
||||
** of those subsystems that were initialized, and
|
||||
** 3) A subsequent call to sqlite3_initialize() attempts to initialize
|
||||
** the remaining, uninitialized, subsystems.
|
||||
*/
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include <string.h>
|
||||
#include <tcl.h>
|
||||
|
||||
static struct Wrapped {
|
||||
sqlite3_pcache_methods2 pcache;
|
||||
sqlite3_mem_methods mem;
|
||||
sqlite3_mutex_methods mutex;
|
||||
|
||||
int mem_init; /* True if mem subsystem is initalized */
|
||||
int mem_fail; /* True to fail mem subsystem inialization */
|
||||
int mutex_init; /* True if mutex subsystem is initalized */
|
||||
int mutex_fail; /* True to fail mutex subsystem inialization */
|
||||
int pcache_init; /* True if pcache subsystem is initalized */
|
||||
int pcache_fail; /* True to fail pcache subsystem inialization */
|
||||
} wrapped;
|
||||
|
||||
static int wrMemInit(void *pAppData){
|
||||
int rc;
|
||||
if( wrapped.mem_fail ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = wrapped.mem.xInit(wrapped.mem.pAppData);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
wrapped.mem_init = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static void wrMemShutdown(void *pAppData){
|
||||
wrapped.mem.xShutdown(wrapped.mem.pAppData);
|
||||
wrapped.mem_init = 0;
|
||||
}
|
||||
static void *wrMemMalloc(int n) {return wrapped.mem.xMalloc(n);}
|
||||
static void wrMemFree(void *p) {wrapped.mem.xFree(p);}
|
||||
static void *wrMemRealloc(void *p, int n) {return wrapped.mem.xRealloc(p, n);}
|
||||
static int wrMemSize(void *p) {return wrapped.mem.xSize(p);}
|
||||
static int wrMemRoundup(int n) {return wrapped.mem.xRoundup(n);}
|
||||
|
||||
|
||||
static int wrMutexInit(void){
|
||||
int rc;
|
||||
if( wrapped.mutex_fail ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = wrapped.mutex.xMutexInit();
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
wrapped.mutex_init = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static int wrMutexEnd(void){
|
||||
wrapped.mutex.xMutexEnd();
|
||||
wrapped.mutex_init = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static sqlite3_mutex *wrMutexAlloc(int e){
|
||||
return wrapped.mutex.xMutexAlloc(e);
|
||||
}
|
||||
static void wrMutexFree(sqlite3_mutex *p){
|
||||
wrapped.mutex.xMutexFree(p);
|
||||
}
|
||||
static void wrMutexEnter(sqlite3_mutex *p){
|
||||
wrapped.mutex.xMutexEnter(p);
|
||||
}
|
||||
static int wrMutexTry(sqlite3_mutex *p){
|
||||
return wrapped.mutex.xMutexTry(p);
|
||||
}
|
||||
static void wrMutexLeave(sqlite3_mutex *p){
|
||||
wrapped.mutex.xMutexLeave(p);
|
||||
}
|
||||
static int wrMutexHeld(sqlite3_mutex *p){
|
||||
return wrapped.mutex.xMutexHeld(p);
|
||||
}
|
||||
static int wrMutexNotheld(sqlite3_mutex *p){
|
||||
return wrapped.mutex.xMutexNotheld(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int wrPCacheInit(void *pArg){
|
||||
int rc;
|
||||
if( wrapped.pcache_fail ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = wrapped.pcache.xInit(wrapped.pcache.pArg);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
wrapped.pcache_init = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static void wrPCacheShutdown(void *pArg){
|
||||
wrapped.pcache.xShutdown(wrapped.pcache.pArg);
|
||||
wrapped.pcache_init = 0;
|
||||
}
|
||||
|
||||
static sqlite3_pcache *wrPCacheCreate(int a, int b, int c){
|
||||
return wrapped.pcache.xCreate(a, b, c);
|
||||
}
|
||||
static void wrPCacheCachesize(sqlite3_pcache *p, int n){
|
||||
wrapped.pcache.xCachesize(p, n);
|
||||
}
|
||||
static int wrPCachePagecount(sqlite3_pcache *p){
|
||||
return wrapped.pcache.xPagecount(p);
|
||||
}
|
||||
static sqlite3_pcache_page *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){
|
||||
return wrapped.pcache.xFetch(p, a, b);
|
||||
}
|
||||
static void wrPCacheUnpin(sqlite3_pcache *p, sqlite3_pcache_page *a, int b){
|
||||
wrapped.pcache.xUnpin(p, a, b);
|
||||
}
|
||||
static void wrPCacheRekey(
|
||||
sqlite3_pcache *p,
|
||||
sqlite3_pcache_page *a,
|
||||
unsigned b,
|
||||
unsigned c
|
||||
){
|
||||
wrapped.pcache.xRekey(p, a, b, c);
|
||||
}
|
||||
static void wrPCacheTruncate(sqlite3_pcache *p, unsigned a){
|
||||
wrapped.pcache.xTruncate(p, a);
|
||||
}
|
||||
static void wrPCacheDestroy(sqlite3_pcache *p){
|
||||
wrapped.pcache.xDestroy(p);
|
||||
}
|
||||
|
||||
static void installInitWrappers(void){
|
||||
sqlite3_mutex_methods mutexmethods = {
|
||||
wrMutexInit, wrMutexEnd, wrMutexAlloc,
|
||||
wrMutexFree, wrMutexEnter, wrMutexTry,
|
||||
wrMutexLeave, wrMutexHeld, wrMutexNotheld
|
||||
};
|
||||
sqlite3_pcache_methods2 pcachemethods = {
|
||||
1, 0,
|
||||
wrPCacheInit, wrPCacheShutdown, wrPCacheCreate,
|
||||
wrPCacheCachesize, wrPCachePagecount, wrPCacheFetch,
|
||||
wrPCacheUnpin, wrPCacheRekey, wrPCacheTruncate,
|
||||
wrPCacheDestroy
|
||||
};
|
||||
sqlite3_mem_methods memmethods = {
|
||||
wrMemMalloc, wrMemFree, wrMemRealloc,
|
||||
wrMemSize, wrMemRoundup, wrMemInit,
|
||||
wrMemShutdown,
|
||||
0
|
||||
};
|
||||
|
||||
memset(&wrapped, 0, sizeof(wrapped));
|
||||
|
||||
sqlite3_shutdown();
|
||||
sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped.mutex);
|
||||
sqlite3_config(SQLITE_CONFIG_GETMALLOC, &wrapped.mem);
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &wrapped.pcache);
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &mutexmethods);
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &memmethods);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcachemethods);
|
||||
}
|
||||
|
||||
static int init_wrapper_install(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
int i;
|
||||
installInitWrappers();
|
||||
for(i=1; i<objc; i++){
|
||||
char *z = Tcl_GetString(objv[i]);
|
||||
if( strcmp(z, "mem")==0 ){
|
||||
wrapped.mem_fail = 1;
|
||||
}else if( strcmp(z, "mutex")==0 ){
|
||||
wrapped.mutex_fail = 1;
|
||||
}else if( strcmp(z, "pcache")==0 ){
|
||||
wrapped.pcache_fail = 1;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "Unknown argument: \"", z, "\"");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_uninstall(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
sqlite3_shutdown();
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped.mutex);
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &wrapped.mem);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &wrapped.pcache);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_clear(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
wrapped.mem_fail = 0;
|
||||
wrapped.mutex_fail = 0;
|
||||
wrapped.pcache_fail = 0;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int init_wrapper_query(
|
||||
ClientData clientData, /* Unused */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
Tcl_Obj *pRet;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
if( wrapped.mutex_init ){
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("mutex", -1));
|
||||
}
|
||||
if( wrapped.mem_init ){
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("mem", -1));
|
||||
}
|
||||
if( wrapped.pcache_init ){
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj("pcache", -1));
|
||||
}
|
||||
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetest_init_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aObjCmd[] = {
|
||||
{"init_wrapper_install", init_wrapper_install},
|
||||
{"init_wrapper_query", init_wrapper_query },
|
||||
{"init_wrapper_uninstall", init_wrapper_uninstall},
|
||||
{"init_wrapper_clear", init_wrapper_clear}
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
** 2009 November 10
|
||||
**
|
||||
** 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 a read-only VIRTUAL TABLE that contains the
|
||||
** content of a C-language array of integer values. See the corresponding
|
||||
** header file for full details.
|
||||
*/
|
||||
#include "test_intarray.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/*
|
||||
** Definition of the sqlite3_intarray object.
|
||||
**
|
||||
** The internal representation of an intarray object is subject
|
||||
** to change, is not externally visible, and should be used by
|
||||
** the implementation of intarray only. This object is opaque
|
||||
** to users.
|
||||
*/
|
||||
struct sqlite3_intarray {
|
||||
int n; /* Number of elements in the array */
|
||||
sqlite3_int64 *a; /* Contents of the array */
|
||||
void (*xFree)(void*); /* Function used to free a[] */
|
||||
};
|
||||
|
||||
/* Objects used internally by the virtual table implementation */
|
||||
typedef struct intarray_vtab intarray_vtab;
|
||||
typedef struct intarray_cursor intarray_cursor;
|
||||
|
||||
/* A intarray table object */
|
||||
struct intarray_vtab {
|
||||
sqlite3_vtab base; /* Base class */
|
||||
sqlite3_intarray *pContent; /* Content of the integer array */
|
||||
};
|
||||
|
||||
/* A intarray cursor object */
|
||||
struct intarray_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class */
|
||||
int i; /* Current cursor position */
|
||||
};
|
||||
|
||||
/*
|
||||
** None of this works unless we have virtual tables.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Free an sqlite3_intarray object.
|
||||
*/
|
||||
static void intarrayFree(sqlite3_intarray *p){
|
||||
if( p->xFree ){
|
||||
p->xFree(p->a);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Table destructor for the intarray module.
|
||||
*/
|
||||
static int intarrayDestroy(sqlite3_vtab *p){
|
||||
intarray_vtab *pVtab = (intarray_vtab*)p;
|
||||
sqlite3_free(pVtab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table constructor for the intarray module.
|
||||
*/
|
||||
static int intarrayCreate(
|
||||
sqlite3 *db, /* Database where module is created */
|
||||
void *pAux, /* clientdata for the module */
|
||||
int argc, /* Number of arguments */
|
||||
const char *const*argv, /* Value for all arguments */
|
||||
sqlite3_vtab **ppVtab, /* Write the new virtual table object here */
|
||||
char **pzErr /* Put error message text here */
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
intarray_vtab *pVtab = sqlite3_malloc(sizeof(intarray_vtab));
|
||||
|
||||
if( pVtab ){
|
||||
memset(pVtab, 0, sizeof(intarray_vtab));
|
||||
pVtab->pContent = (sqlite3_intarray*)pAux;
|
||||
rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)");
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab *)pVtab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new cursor on the intarray table.
|
||||
*/
|
||||
static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc = SQLITE_NOMEM;
|
||||
intarray_cursor *pCur;
|
||||
pCur = sqlite3_malloc(sizeof(intarray_cursor));
|
||||
if( pCur ){
|
||||
memset(pCur, 0, sizeof(intarray_cursor));
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCur;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a intarray table cursor.
|
||||
*/
|
||||
static int intarrayClose(sqlite3_vtab_cursor *cur){
|
||||
intarray_cursor *pCur = (intarray_cursor *)cur;
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve a column of data.
|
||||
*/
|
||||
static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
intarray_cursor *pCur = (intarray_cursor*)cur;
|
||||
intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab;
|
||||
if( pCur->i>=0 && pCur->i<pVtab->pContent->n ){
|
||||
sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve the current rowid.
|
||||
*/
|
||||
static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
intarray_cursor *pCur = (intarray_cursor *)cur;
|
||||
*pRowid = pCur->i;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int intarrayEof(sqlite3_vtab_cursor *cur){
|
||||
intarray_cursor *pCur = (intarray_cursor *)cur;
|
||||
intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab;
|
||||
return pCur->i>=pVtab->pContent->n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance the cursor to the next row.
|
||||
*/
|
||||
static int intarrayNext(sqlite3_vtab_cursor *cur){
|
||||
intarray_cursor *pCur = (intarray_cursor *)cur;
|
||||
pCur->i++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a intarray table cursor.
|
||||
*/
|
||||
static int intarrayFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
intarray_cursor *pCur = (intarray_cursor *)pVtabCursor;
|
||||
pCur->i = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyse the WHERE condition.
|
||||
*/
|
||||
static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely echos method calls into TCL
|
||||
** variables.
|
||||
*/
|
||||
static sqlite3_module intarrayModule = {
|
||||
0, /* iVersion */
|
||||
intarrayCreate, /* xCreate - create a new virtual table */
|
||||
intarrayCreate, /* xConnect - connect to an existing vtab */
|
||||
intarrayBestIndex, /* xBestIndex - find the best query index */
|
||||
intarrayDestroy, /* xDisconnect - disconnect a vtab */
|
||||
intarrayDestroy, /* xDestroy - destroy a vtab */
|
||||
intarrayOpen, /* xOpen - open a cursor */
|
||||
intarrayClose, /* xClose - close a cursor */
|
||||
intarrayFilter, /* xFilter - configure scan constraints */
|
||||
intarrayNext, /* xNext - advance a cursor */
|
||||
intarrayEof, /* xEof */
|
||||
intarrayColumn, /* xColumn - read data */
|
||||
intarrayRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
|
||||
/*
|
||||
** Invoke this routine to create a specific instance of an intarray object.
|
||||
** The new intarray object is returned by the 3rd parameter.
|
||||
**
|
||||
** Each intarray object corresponds to a virtual table in the TEMP table
|
||||
** with a name of zName.
|
||||
**
|
||||
** Destroy the intarray object by dropping the virtual table. If not done
|
||||
** explicitly by the application, the virtual table will be dropped implicitly
|
||||
** by the system when the database connection is closed.
|
||||
*/
|
||||
int sqlite3_intarray_create(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
sqlite3_intarray **ppReturn
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_intarray *p;
|
||||
|
||||
*ppReturn = p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p,
|
||||
(void(*)(void*))intarrayFree);
|
||||
if( rc==SQLITE_OK ){
|
||||
char *zSql;
|
||||
zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q",
|
||||
zName, zName);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Bind a new array array of integers to a specific intarray object.
|
||||
**
|
||||
** The array of integers bound must be unchanged for the duration of
|
||||
** any query against the corresponding virtual table. If the integer
|
||||
** array does change or is deallocated undefined behavior will result.
|
||||
*/
|
||||
int sqlite3_intarray_bind(
|
||||
sqlite3_intarray *pIntArray, /* The intarray object to bind to */
|
||||
int nElements, /* Number of elements in the intarray */
|
||||
sqlite3_int64 *aElements, /* Content of the intarray */
|
||||
void (*xFree)(void*) /* How to dispose of the intarray when done */
|
||||
){
|
||||
if( pIntArray->xFree ){
|
||||
pIntArray->xFree(pIntArray->a);
|
||||
}
|
||||
pIntArray->n = nElements;
|
||||
pIntArray->a = aElements;
|
||||
pIntArray->xFree = xFree;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
** Everything below is interface for testing this module.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
#include <tcl.h>
|
||||
|
||||
/*
|
||||
** Routines to encode and decode pointers
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
extern void *sqlite3TestTextToPtr(const char*);
|
||||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char *zPtr, void*);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/*
|
||||
** sqlite3_intarray_create DB NAME
|
||||
**
|
||||
** Invoke the sqlite3_intarray_create interface. A string that becomes
|
||||
** the first parameter to sqlite3_intarray_bind.
|
||||
*/
|
||||
static int test_intarray_create(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zName;
|
||||
sqlite3_intarray *pArray;
|
||||
int rc = SQLITE_OK;
|
||||
char zPtr[100];
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zName = Tcl_GetString(objv[2]);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_intarray_create(db, zName, &pArray);
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pArray==0 );
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3TestMakePointerStr(interp, zPtr, pArray);
|
||||
Tcl_AppendResult(interp, zPtr, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_intarray_bind INTARRAY ?VALUE ...?
|
||||
**
|
||||
** Invoke the sqlite3_intarray_bind interface on the given array of integers.
|
||||
*/
|
||||
static int test_intarray_bind(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3_intarray *pArray;
|
||||
int rc = SQLITE_OK;
|
||||
int i, n;
|
||||
sqlite3_int64 *a;
|
||||
|
||||
if( objc<2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "INTARRAY");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
pArray = (sqlite3_intarray*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
n = objc - 2;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
a = sqlite3_malloc( sizeof(a[0])*n );
|
||||
if( a==0 ){
|
||||
Tcl_AppendResult(interp, "SQLITE_NOMEM", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(i=0; i<n; i++){
|
||||
Tcl_WideInt x = 0;
|
||||
Tcl_GetWideIntFromObj(0, objv[i+2], &x);
|
||||
a[i] = x;
|
||||
}
|
||||
rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestintarray_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "sqlite3_intarray_create", test_intarray_create, 0 },
|
||||
{ "sqlite3_intarray_bind", test_intarray_bind, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_TEST */
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
** 2009 November 10
|
||||
**
|
||||
** 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 is the C-language interface definition for the "intarray" or
|
||||
** integer array virtual table for SQLite.
|
||||
**
|
||||
** The intarray virtual table is designed to facilitate using an
|
||||
** array of integers as the right-hand side of an IN operator. So
|
||||
** instead of doing a prepared statement like this:
|
||||
**
|
||||
** SELECT * FROM table WHERE x IN (?,?,?,...,?);
|
||||
**
|
||||
** And then binding indivdual integers to each of ? slots, a C-language
|
||||
** application can create an intarray object (named "ex1" in the following
|
||||
** example), prepare a statement like this:
|
||||
**
|
||||
** SELECT * FROM table WHERE x IN ex1;
|
||||
**
|
||||
** Then bind an ordinary C/C++ array of integer values to the ex1 object
|
||||
** to run the statement.
|
||||
**
|
||||
** USAGE:
|
||||
**
|
||||
** One or more intarray objects can be created as follows:
|
||||
**
|
||||
** sqlite3_intarray *p1, *p2, *p3;
|
||||
** sqlite3_intarray_create(db, "ex1", &p1);
|
||||
** sqlite3_intarray_create(db, "ex2", &p2);
|
||||
** sqlite3_intarray_create(db, "ex3", &p3);
|
||||
**
|
||||
** Each call to sqlite3_intarray_create() generates a new virtual table
|
||||
** module and a singleton of that virtual table module in the TEMP
|
||||
** database. Both the module and the virtual table instance use the
|
||||
** name given by the second parameter. The virtual tables can then be
|
||||
** used in prepared statements:
|
||||
**
|
||||
** SELECT * FROM t1, t2, t3
|
||||
** WHERE t1.x IN ex1
|
||||
** AND t2.y IN ex2
|
||||
** AND t3.z IN ex3;
|
||||
**
|
||||
** Each integer array is initially empty. New arrays can be bound to
|
||||
** an integer array as follows:
|
||||
**
|
||||
** sqlite3_int64 a1[] = { 1, 2, 3, 4 };
|
||||
** sqlite3_int64 a2[] = { 5, 6, 7, 8, 9, 10, 11 };
|
||||
** sqlite3_int64 *a3 = sqlite3_malloc( 100*sizeof(sqlite3_int64) );
|
||||
** // Fill in content of a3[]
|
||||
** sqlite3_intarray_bind(p1, 4, a1, 0);
|
||||
** sqlite3_intarray_bind(p2, 7, a2, 0);
|
||||
** sqlite3_intarray_bind(p3, 100, a3, sqlite3_free);
|
||||
**
|
||||
** A single intarray object can be rebound multiple times. But do not
|
||||
** attempt to change the bindings of an intarray while it is in the middle
|
||||
** of a query.
|
||||
**
|
||||
** The array that holds the integers is automatically freed by the function
|
||||
** in the fourth parameter to sqlite3_intarray_bind() when the array is no
|
||||
** longer needed. The application must not change the intarray values
|
||||
** while an intarray is in the middle of a query.
|
||||
**
|
||||
** The intarray object is automatically destroyed when its corresponding
|
||||
** virtual table is dropped. Since the virtual tables are created in the
|
||||
** TEMP database, they are automatically dropped when the database connection
|
||||
** closes so the application does not normally need to take any special
|
||||
** action to free the intarray objects.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#ifndef _INTARRAY_H_
|
||||
#define _INTARRAY_H_
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An sqlite3_intarray is an abstract type to stores an instance of
|
||||
** an integer array.
|
||||
*/
|
||||
typedef struct sqlite3_intarray sqlite3_intarray;
|
||||
|
||||
/*
|
||||
** Invoke this routine to create a specific instance of an intarray object.
|
||||
** The new intarray object is returned by the 3rd parameter.
|
||||
**
|
||||
** Each intarray object corresponds to a virtual table in the TEMP table
|
||||
** with a name of zName.
|
||||
**
|
||||
** Destroy the intarray object by dropping the virtual table. If not done
|
||||
** explicitly by the application, the virtual table will be dropped implicitly
|
||||
** by the system when the database connection is closed.
|
||||
*/
|
||||
int sqlite3_intarray_create(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
sqlite3_intarray **ppReturn
|
||||
);
|
||||
|
||||
/*
|
||||
** Bind a new array array of integers to a specific intarray object.
|
||||
**
|
||||
** The array of integers bound must be unchanged for the duration of
|
||||
** any query against the corresponding virtual table. If the integer
|
||||
** array does change or is deallocated undefined behavior will result.
|
||||
*/
|
||||
int sqlite3_intarray_bind(
|
||||
sqlite3_intarray *pIntArray, /* The intarray object to bind to */
|
||||
int nElements, /* Number of elements in the intarray */
|
||||
sqlite3_int64 *aElements, /* Content of the intarray */
|
||||
void (*xFree)(void*) /* How to dispose of the intarray when done */
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _INTARRAY_H_ */
|
||||
+857
File diff suppressed because it is too large
Load Diff
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
** 2006 June 14
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Test extension for testing the sqlite3_load_extension() function.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** The half() SQL function returns half of its input value.
|
||||
*/
|
||||
static void halfFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_result_double(context, 0.5*sqlite3_value_double(argv[0]));
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL functions to call the sqlite3_status function and return results.
|
||||
*/
|
||||
static void statusFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int op, mx, cur, resetFlag, rc;
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
|
||||
op = sqlite3_value_int(argv[0]);
|
||||
}else if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
|
||||
int i;
|
||||
const char *zName;
|
||||
static const struct {
|
||||
const char *zName;
|
||||
int op;
|
||||
} aOp[] = {
|
||||
{ "MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
|
||||
{ "PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
|
||||
{ "PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
|
||||
{ "SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
|
||||
{ "SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
|
||||
{ "MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
|
||||
};
|
||||
int nOp = sizeof(aOp)/sizeof(aOp[0]);
|
||||
zName = (const char*)sqlite3_value_text(argv[0]);
|
||||
for(i=0; i<nOp; i++){
|
||||
if( strcmp(aOp[i].zName, zName)==0 ){
|
||||
op = aOp[i].op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i>=nOp ){
|
||||
char *zMsg = sqlite3_mprintf("unknown status property: %s", zName);
|
||||
sqlite3_result_error(context, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
sqlite3_result_error(context, "unknown status type", -1);
|
||||
return;
|
||||
}
|
||||
if( argc==2 ){
|
||||
resetFlag = sqlite3_value_int(argv[1]);
|
||||
}else{
|
||||
resetFlag = 0;
|
||||
}
|
||||
rc = sqlite3_status(op, &cur, &mx, resetFlag);
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zMsg = sqlite3_mprintf("sqlite3_status(%d,...) returns %d", op, rc);
|
||||
sqlite3_result_error(context, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
return;
|
||||
}
|
||||
if( argc==2 ){
|
||||
sqlite3_result_int(context, mx);
|
||||
}else{
|
||||
sqlite3_result_int(context, cur);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
int testloadext_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int nErr = 0;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
nErr |= sqlite3_create_function(db, "half", 1, SQLITE_ANY, 0, halfFunc, 0, 0);
|
||||
nErr |= sqlite3_create_function(db, "sqlite3_status", 1, SQLITE_ANY, 0,
|
||||
statusFunc, 0, 0);
|
||||
nErr |= sqlite3_create_function(db, "sqlite3_status", 2, SQLITE_ANY, 0,
|
||||
statusFunc, 0, 0);
|
||||
return nErr ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Another extension entry point. This one always fails.
|
||||
*/
|
||||
int testbrokenext_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
char *zErr;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
zErr = sqlite3_mprintf("broken!");
|
||||
*pzErrMsg = zErr;
|
||||
return 1;
|
||||
}
|
||||
+1494
File diff suppressed because it is too large
Load Diff
+1385
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
** 2011 March 18
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains a VFS "shim" - a layer that sits in between the
|
||||
** pager and the real VFS.
|
||||
**
|
||||
** This particular shim enforces a multiplex system on DB files.
|
||||
** This shim shards/partitions a single DB file into smaller
|
||||
** "chunks" such that the total DB file size may exceed the maximum
|
||||
** file size of the underlying file system.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef _TEST_MULTIPLEX_H
|
||||
#define _TEST_MULTIPLEX_H
|
||||
|
||||
/*
|
||||
** CAPI: File-control Operations Supported by Multiplex VFS
|
||||
**
|
||||
** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle.
|
||||
**
|
||||
** MULTIPLEX_CTRL_ENABLE:
|
||||
** This file control is used to enable or disable the multiplex
|
||||
** shim.
|
||||
**
|
||||
** MULTIPLEX_CTRL_SET_CHUNK_SIZE:
|
||||
** This file control is used to set the maximum allowed chunk
|
||||
** size for a multiplex file set. The chunk size should be
|
||||
** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up
|
||||
** if not.
|
||||
**
|
||||
** MULTIPLEX_CTRL_SET_MAX_CHUNKS:
|
||||
** This file control is used to set the maximum number of chunks
|
||||
** allowed to be used for a mutliplex file set.
|
||||
*/
|
||||
#define MULTIPLEX_CTRL_ENABLE 214014
|
||||
#define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015
|
||||
#define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
|
||||
**
|
||||
** Use the VFS named zOrigVfsName as the VFS that does the actual work.
|
||||
** Use the default if zOrigVfsName==NULL.
|
||||
**
|
||||
** The multiplex VFS shim is named "multiplex". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** An auto-extension is registered which will make the function
|
||||
** multiplex_control() available to database connections. This
|
||||
** function gives access to the xFileControl interface of the
|
||||
** multiplex VFS shim.
|
||||
**
|
||||
** SELECT multiplex_control(<op>,<val>);
|
||||
**
|
||||
** <op>=1 MULTIPLEX_CTRL_ENABLE
|
||||
** <val>=0 disable
|
||||
** <val>=1 enable
|
||||
**
|
||||
** <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE
|
||||
** <val> int, chunk size
|
||||
**
|
||||
** <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS
|
||||
** <val> int, max chunks
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault);
|
||||
|
||||
/*
|
||||
** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining multiplex groups.
|
||||
*/
|
||||
extern int sqlite3_multiplex_shutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* _TEST_MULTIPLEX_H */
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
** 2008 June 18
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains test logic for the sqlite3_mutex interfaces.
|
||||
*/
|
||||
|
||||
#include "tcl.h"
|
||||
#include "sqlite3.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* defined in main.c */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
/* A countable mutex */
|
||||
struct sqlite3_mutex {
|
||||
sqlite3_mutex *pReal;
|
||||
int eType;
|
||||
};
|
||||
|
||||
/* State variables */
|
||||
static struct test_mutex_globals {
|
||||
int isInstalled; /* True if installed */
|
||||
int disableInit; /* True to cause sqlite3_initalize() to fail */
|
||||
int disableTry; /* True to force sqlite3_mutex_try() to fail */
|
||||
int isInit; /* True if initialized */
|
||||
sqlite3_mutex_methods m; /* Interface to "real" mutex system */
|
||||
int aCounter[8]; /* Number of grabs of each type of mutex */
|
||||
sqlite3_mutex aStatic[6]; /* The six static mutexes */
|
||||
} g = {0};
|
||||
|
||||
/* Return true if the countable mutex is currently held */
|
||||
static int counterMutexHeld(sqlite3_mutex *p){
|
||||
return g.m.xMutexHeld(p->pReal);
|
||||
}
|
||||
|
||||
/* Return true if the countable mutex is not currently held */
|
||||
static int counterMutexNotheld(sqlite3_mutex *p){
|
||||
return g.m.xMutexNotheld(p->pReal);
|
||||
}
|
||||
|
||||
/* Initialize the countable mutex interface
|
||||
** Or, if g.disableInit is non-zero, then do not initialize but instead
|
||||
** return the value of g.disableInit as the result code. This can be used
|
||||
** to simulate an initialization failure.
|
||||
*/
|
||||
static int counterMutexInit(void){
|
||||
int rc;
|
||||
if( g.disableInit ) return g.disableInit;
|
||||
rc = g.m.xMutexInit();
|
||||
g.isInit = 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Uninitialize the mutex subsystem
|
||||
*/
|
||||
static int counterMutexEnd(void){
|
||||
g.isInit = 0;
|
||||
return g.m.xMutexEnd();
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a countable mutex
|
||||
*/
|
||||
static sqlite3_mutex *counterMutexAlloc(int eType){
|
||||
sqlite3_mutex *pReal;
|
||||
sqlite3_mutex *pRet = 0;
|
||||
|
||||
assert( g.isInit );
|
||||
assert(eType<8 && eType>=0);
|
||||
|
||||
pReal = g.m.xMutexAlloc(eType);
|
||||
if( !pReal ) return 0;
|
||||
|
||||
if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){
|
||||
pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex));
|
||||
}else{
|
||||
pRet = &g.aStatic[eType-2];
|
||||
}
|
||||
|
||||
pRet->eType = eType;
|
||||
pRet->pReal = pReal;
|
||||
return pRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a countable mutex
|
||||
*/
|
||||
static void counterMutexFree(sqlite3_mutex *p){
|
||||
assert( g.isInit );
|
||||
g.m.xMutexFree(p->pReal);
|
||||
if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter a countable mutex. Block until entry is safe.
|
||||
*/
|
||||
static void counterMutexEnter(sqlite3_mutex *p){
|
||||
assert( g.isInit );
|
||||
g.aCounter[p->eType]++;
|
||||
g.m.xMutexEnter(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to enter a mutex. Return true on success.
|
||||
*/
|
||||
static int counterMutexTry(sqlite3_mutex *p){
|
||||
assert( g.isInit );
|
||||
g.aCounter[p->eType]++;
|
||||
if( g.disableTry ) return SQLITE_BUSY;
|
||||
return g.m.xMutexTry(p->pReal);
|
||||
}
|
||||
|
||||
/* Leave a mutex
|
||||
*/
|
||||
static void counterMutexLeave(sqlite3_mutex *p){
|
||||
assert( g.isInit );
|
||||
g.m.xMutexLeave(p->pReal);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_shutdown
|
||||
*/
|
||||
static int test_shutdown(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_shutdown();
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_initialize
|
||||
*/
|
||||
static int test_initialize(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3_initialize();
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** install_mutex_counters BOOLEAN
|
||||
*/
|
||||
static int test_install_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int isInstall;
|
||||
|
||||
sqlite3_mutex_methods counter_methods = {
|
||||
counterMutexInit,
|
||||
counterMutexEnd,
|
||||
counterMutexAlloc,
|
||||
counterMutexFree,
|
||||
counterMutexEnter,
|
||||
counterMutexTry,
|
||||
counterMutexLeave,
|
||||
counterMutexHeld,
|
||||
counterMutexNotheld
|
||||
};
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
assert(isInstall==0 || isInstall==1);
|
||||
assert(g.isInstalled==0 || g.isInstalled==1);
|
||||
if( isInstall==g.isInstalled ){
|
||||
Tcl_AppendResult(interp, "mutex counters are ", 0);
|
||||
Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( isInstall ){
|
||||
assert( g.m.xMutexAlloc==0 );
|
||||
rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods);
|
||||
}
|
||||
g.disableTry = 0;
|
||||
}else{
|
||||
assert( g.m.xMutexAlloc );
|
||||
rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m);
|
||||
memset(&g.m, 0, sizeof(sqlite3_mutex_methods));
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
g.isInstalled = isInstall;
|
||||
}
|
||||
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** read_mutex_counters
|
||||
*/
|
||||
static int test_read_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
Tcl_Obj *pRet;
|
||||
int ii;
|
||||
char *aName[8] = {
|
||||
"fast", "recursive", "static_master", "static_mem",
|
||||
"static_open", "static_prng", "static_lru", "static_pmem"
|
||||
};
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
for(ii=0; ii<8; ii++){
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1));
|
||||
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii]));
|
||||
}
|
||||
Tcl_SetObjResult(interp, pRet);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** clear_mutex_counters
|
||||
*/
|
||||
static int test_clear_mutex_counters(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int ii;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
for(ii=0; ii<8; ii++){
|
||||
g.aCounter[ii] = 0;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create and free a mutex. Return the mutex pointer. The pointer
|
||||
** will be invalid since the mutex has already been freed. The
|
||||
** return pointer just checks to see if the mutex really was allocated.
|
||||
*/
|
||||
static int test_alloc_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
char zBuf[100];
|
||||
sqlite3_mutex_free(p);
|
||||
sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p);
|
||||
Tcl_AppendResult(interp, zBuf, (char*)0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3_config OPTION
|
||||
**
|
||||
** OPTION can be either one of the keywords:
|
||||
**
|
||||
** SQLITE_CONFIG_SINGLETHREAD
|
||||
** SQLITE_CONFIG_MULTITHREAD
|
||||
** SQLITE_CONFIG_SERIALIZED
|
||||
**
|
||||
** Or OPTION can be an raw integer.
|
||||
*/
|
||||
static int test_config(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
struct ConfigOption {
|
||||
const char *zName;
|
||||
int iValue;
|
||||
} aOpt[] = {
|
||||
{"singlethread", SQLITE_CONFIG_SINGLETHREAD},
|
||||
{"multithread", SQLITE_CONFIG_MULTITHREAD},
|
||||
{"serialized", SQLITE_CONFIG_SERIALIZED},
|
||||
{0, 0}
|
||||
};
|
||||
int s = sizeof(struct ConfigOption);
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){
|
||||
if( Tcl_GetIntFromObj(interp, objv[1], &i) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}else{
|
||||
i = aOpt[i].iValue;
|
||||
}
|
||||
|
||||
rc = sqlite3_config(i);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){
|
||||
sqlite3 *db;
|
||||
Tcl_CmdInfo info;
|
||||
char *zCmd = Tcl_GetString(pObj);
|
||||
if( Tcl_GetCommandInfo(pInterp, zCmd, &info) ){
|
||||
db = *((sqlite3 **)info.objClientData);
|
||||
}else{
|
||||
db = (sqlite3*)sqlite3TestTextToPtr(zCmd);
|
||||
}
|
||||
assert( db );
|
||||
return db;
|
||||
}
|
||||
|
||||
static int test_enter_db_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
db = getDbPointer(interp, objv[1]);
|
||||
if( !db ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int test_leave_db_mutex(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
db = getDbPointer(interp, objv[1]);
|
||||
if( !db ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetest_mutex_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown },
|
||||
{ "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize },
|
||||
{ "sqlite3_config", (Tcl_ObjCmdProc*)test_config },
|
||||
|
||||
{ "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex },
|
||||
{ "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex },
|
||||
|
||||
{ "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex },
|
||||
{ "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters },
|
||||
{ "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters },
|
||||
{ "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
|
||||
}
|
||||
|
||||
Tcl_LinkVar(interp, "disable_mutex_init",
|
||||
(char*)&g.disableInit, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "disable_mutex_try",
|
||||
(char*)&g.disableTry, TCL_LINK_INT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
+830
File diff suppressed because it is too large
Load Diff
+1215
File diff suppressed because it is too large
Load Diff
+467
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
** 2008 November 18
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code used for testing the SQLite system.
|
||||
** None of the code in this file goes into a deliverable build.
|
||||
**
|
||||
** This file contains an application-defined pager cache
|
||||
** implementation that can be plugged in in place of the
|
||||
** default pcache. This alternative pager cache will throw
|
||||
** some errors that the default cache does not.
|
||||
**
|
||||
** This pagecache implementation is designed for simplicity
|
||||
** not speed.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** Global data used by this test implementation. There is no
|
||||
** mutexing, which means this page cache will not work in a
|
||||
** multi-threaded test.
|
||||
*/
|
||||
typedef struct testpcacheGlobalType testpcacheGlobalType;
|
||||
struct testpcacheGlobalType {
|
||||
void *pDummy; /* Dummy allocation to simulate failures */
|
||||
int nInstance; /* Number of current instances */
|
||||
unsigned discardChance; /* Chance of discarding on an unpin (0-100) */
|
||||
unsigned prngSeed; /* Seed for the PRNG */
|
||||
unsigned highStress; /* Call xStress agressively */
|
||||
};
|
||||
static testpcacheGlobalType testpcacheGlobal;
|
||||
|
||||
/*
|
||||
** Initializer.
|
||||
**
|
||||
** Verify that the initializer is only called when the system is
|
||||
** uninitialized. Allocate some memory and report SQLITE_NOMEM if
|
||||
** the allocation fails. This provides a means to test the recovery
|
||||
** from a failed initialization attempt. It also verifies that the
|
||||
** the destructor always gets call - otherwise there would be a
|
||||
** memory leak.
|
||||
*/
|
||||
static int testpcacheInit(void *pArg){
|
||||
assert( pArg==(void*)&testpcacheGlobal );
|
||||
assert( testpcacheGlobal.pDummy==0 );
|
||||
assert( testpcacheGlobal.nInstance==0 );
|
||||
testpcacheGlobal.pDummy = sqlite3_malloc(10);
|
||||
return testpcacheGlobal.pDummy==0 ? SQLITE_NOMEM : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor
|
||||
**
|
||||
** Verify that this is only called after initialization.
|
||||
** Free the memory allocated by the initializer.
|
||||
*/
|
||||
static void testpcacheShutdown(void *pArg){
|
||||
assert( pArg==(void*)&testpcacheGlobal );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance==0 );
|
||||
sqlite3_free( testpcacheGlobal.pDummy );
|
||||
testpcacheGlobal.pDummy = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Number of pages in a cache.
|
||||
**
|
||||
** The number of pages is a hard upper bound in this test module.
|
||||
** If more pages are requested, sqlite3PcacheFetch() returns NULL.
|
||||
**
|
||||
** If testing with in-memory temp tables, provide a larger pcache.
|
||||
** Some of the test cases need this.
|
||||
*/
|
||||
#if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2
|
||||
# define TESTPCACHE_NPAGE 499
|
||||
#else
|
||||
# define TESTPCACHE_NPAGE 217
|
||||
#endif
|
||||
#define TESTPCACHE_RESERVE 17
|
||||
|
||||
/*
|
||||
** Magic numbers used to determine validity of the page cache.
|
||||
*/
|
||||
#define TESTPCACHE_VALID 0x364585fd
|
||||
#define TESTPCACHE_CLEAR 0xd42670d4
|
||||
|
||||
/*
|
||||
** Private implementation of a page cache.
|
||||
*/
|
||||
typedef struct testpcache testpcache;
|
||||
struct testpcache {
|
||||
int szPage; /* Size of each page. Multiple of 8. */
|
||||
int szExtra; /* Size of extra data that accompanies each page */
|
||||
int bPurgeable; /* True if the page cache is purgeable */
|
||||
int nFree; /* Number of unused slots in a[] */
|
||||
int nPinned; /* Number of pinned slots in a[] */
|
||||
unsigned iRand; /* State of the PRNG */
|
||||
unsigned iMagic; /* Magic number for sanity checking */
|
||||
struct testpcachePage {
|
||||
sqlite3_pcache_page page; /* Base class */
|
||||
unsigned key; /* The key for this page. 0 means unallocated */
|
||||
int isPinned; /* True if the page is pinned */
|
||||
} a[TESTPCACHE_NPAGE]; /* All pages in the cache */
|
||||
};
|
||||
|
||||
/*
|
||||
** Get a random number using the PRNG in the given page cache.
|
||||
*/
|
||||
static unsigned testpcacheRandom(testpcache *p){
|
||||
unsigned x = 0;
|
||||
int i;
|
||||
for(i=0; i<4; i++){
|
||||
p->iRand = (p->iRand*69069 + 5);
|
||||
x = (x<<8) | ((p->iRand>>16)&0xff);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Allocate a new page cache instance.
|
||||
*/
|
||||
static sqlite3_pcache *testpcacheCreate(
|
||||
int szPage,
|
||||
int szExtra,
|
||||
int bPurgeable
|
||||
){
|
||||
int nMem;
|
||||
char *x;
|
||||
testpcache *p;
|
||||
int i;
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
szPage = (szPage+7)&~7;
|
||||
nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*(szPage+szExtra);
|
||||
p = sqlite3_malloc( nMem );
|
||||
if( p==0 ) return 0;
|
||||
x = (char*)&p[1];
|
||||
p->szPage = szPage;
|
||||
p->szExtra = szExtra;
|
||||
p->nFree = TESTPCACHE_NPAGE;
|
||||
p->nPinned = 0;
|
||||
p->iRand = testpcacheGlobal.prngSeed;
|
||||
p->bPurgeable = bPurgeable;
|
||||
p->iMagic = TESTPCACHE_VALID;
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++, x += (szPage+szExtra)){
|
||||
p->a[i].key = 0;
|
||||
p->a[i].isPinned = 0;
|
||||
p->a[i].page.pBuf = (void*)x;
|
||||
p->a[i].page.pExtra = (void*)&x[szPage];
|
||||
}
|
||||
testpcacheGlobal.nInstance++;
|
||||
return (sqlite3_pcache*)p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the cache size
|
||||
*/
|
||||
static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of pages in the cache that are being used.
|
||||
** This includes both pinned and unpinned pages.
|
||||
*/
|
||||
static int testpcachePagecount(sqlite3_pcache *pCache){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
return TESTPCACHE_NPAGE - p->nFree;
|
||||
}
|
||||
|
||||
/*
|
||||
** Fetch a page.
|
||||
*/
|
||||
static sqlite3_pcache_page *testpcacheFetch(
|
||||
sqlite3_pcache *pCache,
|
||||
unsigned key,
|
||||
int createFlag
|
||||
){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
int i, j;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
|
||||
/* See if the page is already in cache. Return immediately if it is */
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].key==key ){
|
||||
if( !p->a[i].isPinned ){
|
||||
p->nPinned++;
|
||||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
p->a[i].isPinned = 1;
|
||||
}
|
||||
return &p->a[i].page;
|
||||
}
|
||||
}
|
||||
|
||||
/* If createFlag is 0, never allocate a new page */
|
||||
if( createFlag==0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If no pages are available, always fail */
|
||||
if( p->nPinned==TESTPCACHE_NPAGE ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */
|
||||
if( p->nPinned>=TESTPCACHE_NPAGE-TESTPCACHE_RESERVE && createFlag<2 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do not allocate if highStress is enabled and createFlag is not 2.
|
||||
**
|
||||
** The highStress setting causes pagerStress() to be called much more
|
||||
** often, which exercises the pager logic more intensely.
|
||||
*/
|
||||
if( testpcacheGlobal.highStress && createFlag<2 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find a free page to allocate if there are any free pages.
|
||||
** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2.
|
||||
*/
|
||||
if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){
|
||||
j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
|
||||
if( p->a[j].key==0 ){
|
||||
p->a[j].key = key;
|
||||
p->a[j].isPinned = 1;
|
||||
memset(p->a[j].page.pBuf, 0, p->szPage);
|
||||
memset(p->a[j].page.pExtra, 0, p->szExtra);
|
||||
p->nPinned++;
|
||||
p->nFree--;
|
||||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
return &p->a[j].page;
|
||||
}
|
||||
}
|
||||
|
||||
/* The prior loop always finds a freepage to allocate */
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
/* If this cache is not purgeable then we have to fail.
|
||||
*/
|
||||
if( p->bPurgeable==0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If there are no free pages, recycle a page. The page to
|
||||
** recycle is selected at random from all unpinned pages.
|
||||
*/
|
||||
j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
|
||||
if( p->a[j].key>0 && p->a[j].isPinned==0 ){
|
||||
p->a[j].key = key;
|
||||
p->a[j].isPinned = 1;
|
||||
memset(p->a[j].page.pBuf, 0, p->szPage);
|
||||
memset(p->a[j].page.pExtra, 0, p->szExtra);
|
||||
p->nPinned++;
|
||||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
return &p->a[j].page;
|
||||
}
|
||||
}
|
||||
|
||||
/* The previous loop always finds a page to recycle. */
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unpin a page.
|
||||
*/
|
||||
static void testpcacheUnpin(
|
||||
sqlite3_pcache *pCache,
|
||||
sqlite3_pcache_page *pOldPage,
|
||||
int discard
|
||||
){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
int i;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
|
||||
/* Randomly discard pages as they are unpinned according to the
|
||||
** discardChance setting. If discardChance is 0, the random discard
|
||||
** never happens. If discardChance is 100, it always happens.
|
||||
*/
|
||||
if( p->bPurgeable
|
||||
&& (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100)
|
||||
){
|
||||
discard = 1;
|
||||
}
|
||||
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( &p->a[i].page==pOldPage ){
|
||||
/* The pOldPage pointer always points to a pinned page */
|
||||
assert( p->a[i].isPinned );
|
||||
p->a[i].isPinned = 0;
|
||||
p->nPinned--;
|
||||
assert( p->nPinned>=0 );
|
||||
if( discard ){
|
||||
p->a[i].key = 0;
|
||||
p->nFree++;
|
||||
assert( p->nFree<=TESTPCACHE_NPAGE );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* The pOldPage pointer always points to a valid page */
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Rekey a single page.
|
||||
*/
|
||||
static void testpcacheRekey(
|
||||
sqlite3_pcache *pCache,
|
||||
sqlite3_pcache_page *pOldPage,
|
||||
unsigned oldKey,
|
||||
unsigned newKey
|
||||
){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
int i;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
|
||||
/* If there already exists another page at newKey, verify that
|
||||
** the other page is unpinned and discard it.
|
||||
*/
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].key==newKey ){
|
||||
/* The new key is never a page that is already pinned */
|
||||
assert( p->a[i].isPinned==0 );
|
||||
p->a[i].key = 0;
|
||||
p->nFree++;
|
||||
assert( p->nFree<=TESTPCACHE_NPAGE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the page to be rekeyed and rekey it.
|
||||
*/
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].key==oldKey ){
|
||||
/* The oldKey and pOldPage parameters match */
|
||||
assert( &p->a[i].page==pOldPage );
|
||||
/* Page to be rekeyed must be pinned */
|
||||
assert( p->a[i].isPinned );
|
||||
p->a[i].key = newKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rekey is always given a valid page to work with */
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Truncate the page cache. Every page with a key of iLimit or larger
|
||||
** is discarded.
|
||||
*/
|
||||
static void testpcacheTruncate(sqlite3_pcache *pCache, unsigned iLimit){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
unsigned int i;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].key>=iLimit ){
|
||||
p->a[i].key = 0;
|
||||
if( p->a[i].isPinned ){
|
||||
p->nPinned--;
|
||||
assert( p->nPinned>=0 );
|
||||
}
|
||||
p->nFree++;
|
||||
assert( p->nFree<=TESTPCACHE_NPAGE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Destroy a page cache.
|
||||
*/
|
||||
static void testpcacheDestroy(sqlite3_pcache *pCache){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
p->iMagic = TESTPCACHE_CLEAR;
|
||||
sqlite3_free(p);
|
||||
testpcacheGlobal.nInstance--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Invoke this routine to register or unregister the testing pager cache
|
||||
** implemented by this file.
|
||||
**
|
||||
** Install the test pager cache if installFlag is 1 and uninstall it if
|
||||
** installFlag is 0.
|
||||
**
|
||||
** When installing, discardChance is a number between 0 and 100 that
|
||||
** indicates the probability of discarding a page when unpinning the
|
||||
** page. 0 means never discard (unless the discard flag is set).
|
||||
** 100 means always discard.
|
||||
*/
|
||||
void installTestPCache(
|
||||
int installFlag, /* True to install. False to uninstall. */
|
||||
unsigned discardChance, /* 0-100. Chance to discard on unpin */
|
||||
unsigned prngSeed, /* Seed for the PRNG */
|
||||
unsigned highStress /* Call xStress agressively */
|
||||
){
|
||||
static const sqlite3_pcache_methods2 testPcache = {
|
||||
1,
|
||||
(void*)&testpcacheGlobal,
|
||||
testpcacheInit,
|
||||
testpcacheShutdown,
|
||||
testpcacheCreate,
|
||||
testpcacheCachesize,
|
||||
testpcachePagecount,
|
||||
testpcacheFetch,
|
||||
testpcacheUnpin,
|
||||
testpcacheRekey,
|
||||
testpcacheTruncate,
|
||||
testpcacheDestroy,
|
||||
};
|
||||
static sqlite3_pcache_methods2 defaultPcache;
|
||||
static int isInstalled = 0;
|
||||
|
||||
assert( testpcacheGlobal.nInstance==0 );
|
||||
assert( testpcacheGlobal.pDummy==0 );
|
||||
assert( discardChance<=100 );
|
||||
testpcacheGlobal.discardChance = discardChance;
|
||||
testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16);
|
||||
testpcacheGlobal.highStress = highStress;
|
||||
if( installFlag!=isInstalled ){
|
||||
if( installFlag ){
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &defaultPcache);
|
||||
assert( defaultPcache.xCreate!=testpcacheCreate );
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &testPcache);
|
||||
}else{
|
||||
assert( defaultPcache.xCreate!=0 );
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultPcache);
|
||||
}
|
||||
isInstalled = installFlag;
|
||||
}
|
||||
}
|
||||
+2008
File diff suppressed because it is too large
Load Diff
+274
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
** 2011 December 1
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains the interface definition for the quota a VFS shim.
|
||||
**
|
||||
** This particular shim enforces a quota system on files. One or more
|
||||
** database files are in a "quota group" that is defined by a GLOB
|
||||
** pattern. A quota is set for the combined size of all files in the
|
||||
** the group. A quota of zero means "no limit". If the total size
|
||||
** of all files in the quota group is greater than the limit, then
|
||||
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
|
||||
**
|
||||
** However, before returning SQLITE_FULL, the write requests invoke
|
||||
** a callback function that is configurable for each quota group.
|
||||
** This callback has the opportunity to enlarge the quota. If the
|
||||
** callback does enlarge the quota such that the total size of all
|
||||
** files within the group is less than the new quota, then the write
|
||||
** continues as if nothing had happened.
|
||||
*/
|
||||
#ifndef _QUOTA_H_
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#if SQLITE_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
/* Make this callable from C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The quota VFS shim is named "quota". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
|
||||
|
||||
/*
|
||||
** Shutdown the quota system.
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining quota groups.
|
||||
*/
|
||||
int sqlite3_quota_shutdown(void);
|
||||
|
||||
/*
|
||||
** Create or destroy a quota group.
|
||||
**
|
||||
** The quota group is defined by the zPattern. When calling this routine
|
||||
** with a zPattern for a quota group that already exists, this routine
|
||||
** merely updates the iLimit, xCallback, and pArg values for that quota
|
||||
** group. If zPattern is new, then a new quota group is created.
|
||||
**
|
||||
** The zPattern is always compared against the full pathname of the file.
|
||||
** Even if APIs are called with relative pathnames, SQLite converts the
|
||||
** name to a full pathname before comparing it against zPattern. zPattern
|
||||
** is a glob pattern with the following matching rules:
|
||||
**
|
||||
** '*' Matches any sequence of zero or more characters.
|
||||
**
|
||||
** '?' Matches exactly one character.
|
||||
**
|
||||
** [...] Matches one character from the enclosed list of
|
||||
** characters. "]" can be part of the list if it is
|
||||
** the first character. Within the list "X-Y" matches
|
||||
** characters X or Y or any character in between the
|
||||
** two. Ex: "[0-9]" matches any digit.
|
||||
**
|
||||
** [^...] Matches one character not in the enclosed list.
|
||||
**
|
||||
** / Matches either / or \. This allows glob patterns
|
||||
** containing / to work on both unix and windows.
|
||||
**
|
||||
** Note that, unlike unix shell globbing, the directory separator "/"
|
||||
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
|
||||
** matches any files anywhere in the directory hierarchy beneath
|
||||
** /abc/xyz.
|
||||
**
|
||||
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
|
||||
** matched as if each byte were a separate character.
|
||||
**
|
||||
** If the iLimit for a quota group is set to zero, then the quota group
|
||||
** is disabled and will be deleted when the last database connection using
|
||||
** the quota group is closed.
|
||||
**
|
||||
** Calling this routine on a zPattern that does not exist and with a
|
||||
** zero iLimit is a no-op.
|
||||
**
|
||||
** A quota group must exist with a non-zero iLimit prior to opening
|
||||
** database connections if those connections are to participate in the
|
||||
** quota group. Creating a quota group does not affect database connections
|
||||
** that are already open.
|
||||
**
|
||||
** The patterns that define the various quota groups should be distinct.
|
||||
** If the same filename matches more than one quota group pattern, then
|
||||
** the behavior of this package is undefined.
|
||||
*/
|
||||
int sqlite3_quota_set(
|
||||
const char *zPattern, /* The filename pattern */
|
||||
sqlite3_int64 iLimit, /* New quota to set for this quota group */
|
||||
void (*xCallback)( /* Callback invoked when going over quota */
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
),
|
||||
void *pArg, /* client data passed thru to callback */
|
||||
void (*xDestroy)(void*) /* Optional destructor for pArg */
|
||||
);
|
||||
|
||||
/*
|
||||
** Bring the named file under quota management, assuming its name matches
|
||||
** the glob pattern of some quota group. Or if it is already under
|
||||
** management, update its size. If zFilename does not match the glob
|
||||
** pattern of any quota group, this routine is a no-op.
|
||||
*/
|
||||
int sqlite3_quota_file(const char *zFilename);
|
||||
|
||||
/*
|
||||
** The following object serves the same role as FILE in the standard C
|
||||
** library. It represents an open connection to a file on disk for I/O.
|
||||
**
|
||||
** A single quota_FILE should not be used by two or more threads at the
|
||||
** same time. Multiple threads can be using different quota_FILE objects
|
||||
** simultaneously, but not the same quota_FILE object.
|
||||
*/
|
||||
typedef struct quota_FILE quota_FILE;
|
||||
|
||||
/*
|
||||
** Create a new quota_FILE object used to read and/or write to the
|
||||
** file zFilename. The zMode parameter is as with standard library zMode.
|
||||
*/
|
||||
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
|
||||
|
||||
/*
|
||||
** Perform I/O against a quota_FILE object. When doing writes, the
|
||||
** quota mechanism may result in a short write, in order to prevent
|
||||
** the sum of sizes of all files from going over quota.
|
||||
*/
|
||||
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
|
||||
size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
|
||||
|
||||
/*
|
||||
** Flush all written content held in memory buffers out to disk.
|
||||
** This is the equivalent of fflush() in the standard library.
|
||||
**
|
||||
** If the hardSync parameter is true (non-zero) then this routine
|
||||
** also forces OS buffers to disk - the equivalent of fsync().
|
||||
**
|
||||
** This routine return zero on success and non-zero if something goes
|
||||
** wrong.
|
||||
*/
|
||||
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
|
||||
|
||||
/*
|
||||
** Close a quota_FILE object and free all associated resources. The
|
||||
** file remains under quota management.
|
||||
*/
|
||||
int sqlite3_quota_fclose(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Move the read/write pointer for a quota_FILE object. Or tell the
|
||||
** current location of the read/write pointer.
|
||||
*/
|
||||
int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||
void sqlite3_quota_rewind(quota_FILE*);
|
||||
long sqlite3_quota_ftell(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Test the error indicator for the given file.
|
||||
**
|
||||
** Return non-zero if the error indicator is set.
|
||||
*/
|
||||
int sqlite3_quota_ferror(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
|
||||
** zero on success and non-zero on any kind of failure.
|
||||
**
|
||||
** The newSize argument must be less than or equal to the current file size.
|
||||
** Any attempt to "truncate" a file to a larger size results in
|
||||
** undefined behavior.
|
||||
*/
|
||||
int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
|
||||
|
||||
/*
|
||||
** Return the last modification time of the opened file, in seconds
|
||||
** since 1970.
|
||||
*/
|
||||
int sqlite3_quota_file_mtime(quota_FILE*, time_t *pTime);
|
||||
|
||||
/*
|
||||
** Return the size of the file as it is known to the quota system.
|
||||
**
|
||||
** This size might be different from the true size of the file on
|
||||
** disk if some outside process has modified the file without using the
|
||||
** quota mechanism, or if calls to sqlite3_quota_fwrite() have occurred
|
||||
** which have increased the file size, but those writes have not yet been
|
||||
** forced to disk using sqlite3_quota_fflush().
|
||||
**
|
||||
** Return -1 if the file is not participating in quota management.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Return the true size of the file.
|
||||
**
|
||||
** The true size should be the same as the size of the file as known
|
||||
** to the quota system, however the sizes might be different if the
|
||||
** file has been extended or truncated via some outside process or if
|
||||
** pending writes have not yet been flushed to disk.
|
||||
**
|
||||
** Return -1 if the file does not exist or if the size of the file
|
||||
** cannot be determined for some reason.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Determine the amount of data in bytes available for reading
|
||||
** in the given file.
|
||||
**
|
||||
** Return -1 if the amount cannot be determined for some reason.
|
||||
*/
|
||||
long sqlite3_quota_file_available(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Delete a file from the disk, if that file is under quota management.
|
||||
** Adjust quotas accordingly.
|
||||
**
|
||||
** If zFilename is the name of a directory that matches one of the
|
||||
** quota glob patterns, then all files under quota management that
|
||||
** are contained within that directory are deleted.
|
||||
**
|
||||
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
|
||||
** When deleting a directory of files, if the deletion of any one
|
||||
** file fails (for example due to an I/O error), then this routine
|
||||
** returns immediately, with the error code, and does not try to
|
||||
** delete any of the other files in the specified directory.
|
||||
**
|
||||
** All files are removed from quota management and deleted from disk.
|
||||
** However, no attempt is made to remove empty directories.
|
||||
**
|
||||
** This routine is a no-op for files that are not under quota management.
|
||||
*/
|
||||
int sqlite3_quota_remove(const char *zFilename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _QUOTA_H_ */
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
** 2010 August 28
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing all sorts of SQLite interfaces. This code
|
||||
** is not included in the SQLite library.
|
||||
*/
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <tcl.h>
|
||||
|
||||
/* Solely for the UNUSED_PARAMETER() macro. */
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
/*
|
||||
** Type used to cache parameter information for the "circle" r-tree geometry
|
||||
** callback.
|
||||
*/
|
||||
typedef struct Circle Circle;
|
||||
struct Circle {
|
||||
struct Box {
|
||||
double xmin;
|
||||
double xmax;
|
||||
double ymin;
|
||||
double ymax;
|
||||
} aBox[2];
|
||||
double centerx;
|
||||
double centery;
|
||||
double radius;
|
||||
};
|
||||
|
||||
/*
|
||||
** Destructor function for Circle objects allocated by circle_geom().
|
||||
*/
|
||||
static void circle_del(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of "circle" r-tree geometry callback.
|
||||
*/
|
||||
static int circle_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
int *pRes
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
Circle *pCircle; /* Structure defining circular region */
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
|
||||
if( p->pUser==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
/* This geometry callback is for use with a 2-dimensional r-tree table.
|
||||
** Return an error if the table does not have exactly 2 dimensions. */
|
||||
if( nCoord!=4 ) return SQLITE_ERROR;
|
||||
|
||||
/* Test that the correct number of parameters (3) have been supplied,
|
||||
** and that the parameters are in range (that the radius of the circle
|
||||
** radius is greater than zero). */
|
||||
if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
|
||||
|
||||
/* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
|
||||
** if the allocation fails. */
|
||||
pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
|
||||
if( !pCircle ) return SQLITE_NOMEM;
|
||||
p->xDelUser = circle_del;
|
||||
|
||||
/* Record the center and radius of the circular region. One way that
|
||||
** tested bounding boxes that intersect the circular region are detected
|
||||
** is by testing if each corner of the bounding box lies within radius
|
||||
** units of the center of the circle. */
|
||||
pCircle->centerx = p->aParam[0];
|
||||
pCircle->centery = p->aParam[1];
|
||||
pCircle->radius = p->aParam[2];
|
||||
|
||||
/* Define two bounding box regions. The first, aBox[0], extends to
|
||||
** infinity in the X dimension. It covers the same range of the Y dimension
|
||||
** as the circular region. The second, aBox[1], extends to infinity in
|
||||
** the Y dimension and is constrained to the range of the circle in the
|
||||
** X dimension.
|
||||
**
|
||||
** Then imagine each box is split in half along its short axis by a line
|
||||
** that intersects the center of the circular region. A bounding box
|
||||
** being tested can be said to intersect the circular region if it contains
|
||||
** points from each half of either of the two infinite bounding boxes.
|
||||
*/
|
||||
pCircle->aBox[0].xmin = pCircle->centerx;
|
||||
pCircle->aBox[0].xmax = pCircle->centerx;
|
||||
pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
|
||||
pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
|
||||
pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
}
|
||||
|
||||
pCircle = (Circle *)p->pUser;
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
** return SQLITE_OK in this case. */
|
||||
for(i=0; i<4; i++){
|
||||
double x = (i&0x01) ? xmax : xmin;
|
||||
double y = (i&0x02) ? ymax : ymin;
|
||||
double d2;
|
||||
|
||||
d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
|
||||
d2 += (y-pCircle->centery)*(y-pCircle->centery);
|
||||
if( d2<(pCircle->radius*pCircle->radius) ){
|
||||
*pRes = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the bounding box covers any other part of the circular region.
|
||||
** See comments above for a description of how this test works. If it does
|
||||
** cover part of the circular region, set the output variable to true
|
||||
** and return SQLITE_OK. */
|
||||
for(i=0; i<2; i++){
|
||||
if( xmin<=pCircle->aBox[i].xmin
|
||||
&& xmax>=pCircle->aBox[i].xmax
|
||||
&& ymin<=pCircle->aBox[i].ymin
|
||||
&& ymax>=pCircle->aBox[i].ymax
|
||||
){
|
||||
*pRes = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* The specified bounding box does not intersect the circular region. Set
|
||||
** the output variable to zero and return SQLITE_OK. */
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* END of implementation of "circle" geometry callback.
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include "tcl.h"
|
||||
|
||||
typedef struct Cube Cube;
|
||||
struct Cube {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double width;
|
||||
double height;
|
||||
double depth;
|
||||
};
|
||||
|
||||
static void cube_context_free(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** The context pointer registered along with the 'cube' callback is
|
||||
** always ((void *)&gHere). This is just to facilitate testing, it is not
|
||||
** actually used for anything.
|
||||
*/
|
||||
static int gHere = 42;
|
||||
|
||||
/*
|
||||
** Implementation of a simple r-tree geom callback to test for intersection
|
||||
** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
|
||||
** coordinates as follows:
|
||||
**
|
||||
** cube(x, y, z, width, height, depth)
|
||||
**
|
||||
** The width, height and depth parameters must all be greater than zero.
|
||||
*/
|
||||
static int cube_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
|
||||
assert( p->pContext==(void *)&gHere );
|
||||
|
||||
if( pCube==0 ){
|
||||
if( p->nParam!=6 || nCoord!=6
|
||||
|| p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
|
||||
){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
|
||||
if( !pCube ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCube->x = p->aParam[0];
|
||||
pCube->y = p->aParam[1];
|
||||
pCube->z = p->aParam[2];
|
||||
pCube->width = p->aParam[3];
|
||||
pCube->height = p->aParam[4];
|
||||
pCube->depth = p->aParam[5];
|
||||
|
||||
p->pUser = (void *)pCube;
|
||||
p->xDelUser = cube_context_free;
|
||||
}
|
||||
|
||||
assert( nCoord==6 );
|
||||
*piRes = 0;
|
||||
if( aCoord[0]<=(pCube->x+pCube->width)
|
||||
&& aCoord[1]>=pCube->x
|
||||
&& aCoord[2]<=(pCube->y+pCube->height)
|
||||
&& aCoord[3]>=pCube->y
|
||||
&& aCoord[4]<=(pCube->z+pCube->depth)
|
||||
&& aCoord[5]>=pCube->z
|
||||
){
|
||||
*piRes = 1;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_RTREE */
|
||||
|
||||
static int register_cube_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_ENABLE_RTREE
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(interp);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
#else
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int register_circle_geom(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
#ifndef SQLITE_ENABLE_RTREE
|
||||
UNUSED_PARAMETER(clientData);
|
||||
UNUSED_PARAMETER(interp);
|
||||
UNUSED_PARAMETER(objc);
|
||||
UNUSED_PARAMETER(objv);
|
||||
#else
|
||||
extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
|
||||
extern const char *sqlite3ErrName(int);
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int Sqlitetestrtree_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
|
||||
Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0);
|
||||
return TCL_OK;
|
||||
}
|
||||
+362
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
** 2006 June 10
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table interfaces. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
*/
|
||||
|
||||
/* The code in this file defines a sqlite3 virtual-table module that
|
||||
** provides a read-only view of the current database schema. There is one
|
||||
** row in the schema table for each column in the database schema.
|
||||
*/
|
||||
#define SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
"database," /* Name of database (i.e. main, temp etc.) */ \
|
||||
"tablename," /* Name of table */ \
|
||||
"cid," /* Column number (from left-to-right, 0 upward) */ \
|
||||
"name," /* Column name */ \
|
||||
"type," /* Specified type (i.e. VARCHAR(32)) */ \
|
||||
"not_null," /* Boolean. True if NOT NULL was specified */ \
|
||||
"dflt_value," /* Default value for this column */ \
|
||||
"pk" /* True if this column is part of the primary key */ \
|
||||
")"
|
||||
|
||||
/* If SQLITE_TEST is defined this code is preprocessed for use as part
|
||||
** of the sqlite test binary "testfixture". Otherwise it is preprocessed
|
||||
** to be compiled into an sqlite dynamic extension.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#else
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct schema_vtab schema_vtab;
|
||||
typedef struct schema_cursor schema_cursor;
|
||||
|
||||
/* A schema table object */
|
||||
struct schema_vtab {
|
||||
sqlite3_vtab base;
|
||||
sqlite3 *db;
|
||||
};
|
||||
|
||||
/* A schema table cursor object */
|
||||
struct schema_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
sqlite3_stmt *pDbList;
|
||||
sqlite3_stmt *pTableList;
|
||||
sqlite3_stmt *pColumnList;
|
||||
int rowid;
|
||||
};
|
||||
|
||||
/*
|
||||
** None of this works unless we have virtual tables.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Table destructor for the schema module.
|
||||
*/
|
||||
static int schemaDestroy(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table constructor for the schema module.
|
||||
*/
|
||||
static int schemaCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schema_vtab *pVtab = sqlite3_malloc(sizeof(schema_vtab));
|
||||
if( pVtab ){
|
||||
memset(pVtab, 0, sizeof(schema_vtab));
|
||||
pVtab->db = db;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_declare_vtab(db, SCHEMA);
|
||||
#endif
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab *)pVtab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new cursor on the schema table.
|
||||
*/
|
||||
static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc = SQLITE_NOMEM;
|
||||
schema_cursor *pCur;
|
||||
pCur = sqlite3_malloc(sizeof(schema_cursor));
|
||||
if( pCur ){
|
||||
memset(pCur, 0, sizeof(schema_cursor));
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCur;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a schema table cursor.
|
||||
*/
|
||||
static int schemaClose(sqlite3_vtab_cursor *cur){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
sqlite3_finalize(pCur->pDbList);
|
||||
sqlite3_finalize(pCur->pTableList);
|
||||
sqlite3_finalize(pCur->pColumnList);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve a column of data.
|
||||
*/
|
||||
static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
switch( i ){
|
||||
case 0:
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pDbList, 1));
|
||||
break;
|
||||
case 1:
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pTableList, 0));
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pColumnList, i-2));
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retrieve the current rowid.
|
||||
*/
|
||||
static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
*pRowid = pCur->rowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int finalize(sqlite3_stmt **ppStmt){
|
||||
int rc = sqlite3_finalize(*ppStmt);
|
||||
*ppStmt = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int schemaEof(sqlite3_vtab_cursor *cur){
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
return (pCur->pDbList ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance the cursor to the next row.
|
||||
*/
|
||||
static int schemaNext(sqlite3_vtab_cursor *cur){
|
||||
int rc = SQLITE_OK;
|
||||
schema_cursor *pCur = (schema_cursor *)cur;
|
||||
schema_vtab *pVtab = (schema_vtab *)(cur->pVtab);
|
||||
char *zSql = 0;
|
||||
|
||||
while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){
|
||||
if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit;
|
||||
|
||||
while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){
|
||||
if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit;
|
||||
|
||||
assert(pCur->pDbList);
|
||||
while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){
|
||||
rc = finalize(&pCur->pDbList);
|
||||
goto next_exit;
|
||||
}
|
||||
|
||||
/* Set zSql to the SQL to pull the list of tables from the
|
||||
** sqlite_master (or sqlite_temp_master) table of the database
|
||||
** identfied by the row pointed to by the SQL statement pCur->pDbList
|
||||
** (iterating through a "PRAGMA database_list;" statement).
|
||||
*/
|
||||
if( sqlite3_column_int(pCur->pDbList, 0)==1 ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT name FROM sqlite_temp_master WHERE type='table'"
|
||||
);
|
||||
}else{
|
||||
sqlite3_stmt *pDbList = pCur->pDbList;
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT name FROM %Q.sqlite_master WHERE type='table'",
|
||||
sqlite3_column_text(pDbList, 1)
|
||||
);
|
||||
}
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto next_exit;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ) goto next_exit;
|
||||
}
|
||||
|
||||
/* Set zSql to the SQL to the table_info pragma for the table currently
|
||||
** identified by the rows pointed to by statements pCur->pDbList and
|
||||
** pCur->pTableList.
|
||||
*/
|
||||
zSql = sqlite3_mprintf("PRAGMA %Q.table_info(%Q)",
|
||||
sqlite3_column_text(pCur->pDbList, 1),
|
||||
sqlite3_column_text(pCur->pTableList, 0)
|
||||
);
|
||||
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto next_exit;
|
||||
}
|
||||
rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK ) goto next_exit;
|
||||
}
|
||||
pCur->rowid++;
|
||||
|
||||
next_exit:
|
||||
/* TODO: Handle rc */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a schema table cursor.
|
||||
*/
|
||||
static int schemaFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
int rc;
|
||||
schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab);
|
||||
schema_cursor *pCur = (schema_cursor *)pVtabCursor;
|
||||
pCur->rowid = 0;
|
||||
finalize(&pCur->pTableList);
|
||||
finalize(&pCur->pColumnList);
|
||||
finalize(&pCur->pDbList);
|
||||
rc = sqlite3_prepare(pVtab->db,"PRAGMA database_list", -1, &pCur->pDbList, 0);
|
||||
return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyse the WHERE condition.
|
||||
*/
|
||||
static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely echos method calls into TCL
|
||||
** variables.
|
||||
*/
|
||||
static sqlite3_module schemaModule = {
|
||||
0, /* iVersion */
|
||||
schemaCreate,
|
||||
schemaCreate,
|
||||
schemaBestIndex,
|
||||
schemaDestroy,
|
||||
schemaDestroy,
|
||||
schemaOpen, /* xOpen - open a cursor */
|
||||
schemaClose, /* xClose - close a cursor */
|
||||
schemaFilter, /* xFilter - configure scan constraints */
|
||||
schemaNext, /* xNext - advance a cursor */
|
||||
schemaEof, /* xEof */
|
||||
schemaColumn, /* xColumn - read data */
|
||||
schemaRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the schema virtual table module.
|
||||
*/
|
||||
static int register_schema_module(
|
||||
ClientData clientData, /* Not used */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "schema", &schemaModule, 0);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetestschema_Init(Tcl_Interp *interp){
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_schema_module", register_schema_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
** Extension load function.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_schema_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "schema", &schemaModule, 0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
+516
File diff suppressed because it is too large
Load Diff
+507
File diff suppressed because it is too large
Load Diff
+639
File diff suppressed because it is too large
Load Diff
+356
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
** 2010 November 19
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Example code for obtaining an exclusive lock on an SQLite database
|
||||
** file. This method is complicated, but works for both WAL and rollback
|
||||
** mode database files. The interface to the example code in this file
|
||||
** consists of the following two functions:
|
||||
**
|
||||
** sqlite3demo_superlock()
|
||||
** sqlite3demo_superunlock()
|
||||
*/
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string.h> /* memset(), strlen() */
|
||||
#include <assert.h> /* assert() */
|
||||
|
||||
/*
|
||||
** A structure to collect a busy-handler callback and argument and a count
|
||||
** of the number of times it has been invoked.
|
||||
*/
|
||||
struct SuperlockBusy {
|
||||
int (*xBusy)(void*,int); /* Pointer to busy-handler function */
|
||||
void *pBusyArg; /* First arg to pass to xBusy */
|
||||
int nBusy; /* Number of times xBusy has been invoked */
|
||||
};
|
||||
typedef struct SuperlockBusy SuperlockBusy;
|
||||
|
||||
/*
|
||||
** An instance of the following structure is allocated for each active
|
||||
** superlock. The opaque handle returned by sqlite3demo_superlock() is
|
||||
** actually a pointer to an instance of this structure.
|
||||
*/
|
||||
struct Superlock {
|
||||
sqlite3 *db; /* Database handle used to lock db */
|
||||
int bWal; /* True if db is a WAL database */
|
||||
};
|
||||
typedef struct Superlock Superlock;
|
||||
|
||||
/*
|
||||
** The pCtx pointer passed to this function is actually a pointer to a
|
||||
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
|
||||
** by the structure and return the result.
|
||||
*/
|
||||
static int superlockBusyHandler(void *pCtx, int UNUSED){
|
||||
SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
|
||||
if( pBusy->xBusy==0 ) return 0;
|
||||
return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to determine if the main database file for
|
||||
** connection db is open in WAL mode or not. If no error occurs and the
|
||||
** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
|
||||
** If it is not in WAL mode, set *pbWal to false.
|
||||
**
|
||||
** If an error occurs, return an SQLite error code. The value of *pbWal
|
||||
** is undefined in this case.
|
||||
*/
|
||||
static int superlockIsWal(Superlock *pLock){
|
||||
int rc; /* Return Code */
|
||||
sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */
|
||||
|
||||
rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
pLock->bWal = 0;
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
|
||||
pLock->bWal = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
|
||||
** of the file fd. If the lock cannot be obtained immediately, invoke
|
||||
** the busy-handler until either it is obtained or the busy-handler
|
||||
** callback returns 0.
|
||||
*/
|
||||
static int superlockShmLock(
|
||||
sqlite3_file *fd, /* Database file handle */
|
||||
int idx, /* Offset of shm-lock to obtain */
|
||||
int nByte, /* Number of consective bytes to lock */
|
||||
SuperlockBusy *pBusy /* Busy-handler wrapper object */
|
||||
){
|
||||
int rc;
|
||||
int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
|
||||
do {
|
||||
rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
|
||||
}while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain the extra locks on the database file required for WAL databases.
|
||||
** Invoke the supplied busy-handler as required.
|
||||
*/
|
||||
static int superlockWalLock(
|
||||
sqlite3 *db, /* Database handle open on WAL database */
|
||||
SuperlockBusy *pBusy /* Busy handler wrapper object */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
sqlite3_file *fd = 0; /* Main database file handle */
|
||||
void volatile *p = 0; /* Pointer to first page of shared memory */
|
||||
|
||||
/* Obtain a pointer to the sqlite3_file object open on the main db file. */
|
||||
rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Obtain the "recovery" lock. Normally, this lock is only obtained by
|
||||
** clients running database recovery.
|
||||
*/
|
||||
rc = superlockShmLock(fd, 2, 1, pBusy);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Zero the start of the first shared-memory page. This means that any
|
||||
** clients that open read or write transactions from this point on will
|
||||
** have to run recovery before proceeding. Since they need the "recovery"
|
||||
** lock that this process is holding to do that, no new read or write
|
||||
** transactions may now be opened. Nor can a checkpoint be run, for the
|
||||
** same reason.
|
||||
*/
|
||||
rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
memset((void *)p, 0, 32);
|
||||
|
||||
/* Obtain exclusive locks on all the "read-lock" slots. Once these locks
|
||||
** are held, it is guaranteed that there are no active reader, writer or
|
||||
** checkpointer clients.
|
||||
*/
|
||||
rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Release a superlock held on a database file. The argument passed to
|
||||
** this function must have been obtained from a successful call to
|
||||
** sqlite3demo_superlock().
|
||||
*/
|
||||
void sqlite3demo_superunlock(void *pLock){
|
||||
Superlock *p = (Superlock *)pLock;
|
||||
if( p->bWal ){
|
||||
int rc; /* Return code */
|
||||
int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
|
||||
sqlite3_file *fd = 0;
|
||||
rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xShmLock(fd, 2, 1, flags);
|
||||
fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
|
||||
}
|
||||
}
|
||||
sqlite3_close(p->db);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain a superlock on the database file identified by zPath, using the
|
||||
** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
|
||||
** returned and output variable *ppLock is populated with an opaque handle
|
||||
** that may be used with sqlite3demo_superunlock() to release the lock.
|
||||
**
|
||||
** If an error occurs, *ppLock is set to 0 and an SQLite error code
|
||||
** (e.g. SQLITE_BUSY) is returned.
|
||||
**
|
||||
** If a required lock cannot be obtained immediately and the xBusy parameter
|
||||
** to this function is not NULL, then xBusy is invoked in the same way
|
||||
** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
|
||||
** until either the lock can be obtained or the busy-handler function returns
|
||||
** 0 (indicating "give up").
|
||||
*/
|
||||
int sqlite3demo_superlock(
|
||||
const char *zPath, /* Path to database file to lock */
|
||||
const char *zVfs, /* VFS to use to access database file */
|
||||
int (*xBusy)(void*,int), /* Busy handler callback */
|
||||
void *pBusyArg, /* Context arg for busy handler */
|
||||
void **ppLock /* OUT: Context to pass to superunlock() */
|
||||
){
|
||||
SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
|
||||
int rc; /* Return code */
|
||||
Superlock *pLock;
|
||||
|
||||
pLock = sqlite3_malloc(sizeof(Superlock));
|
||||
if( !pLock ) return SQLITE_NOMEM;
|
||||
memset(pLock, 0, sizeof(Superlock));
|
||||
|
||||
/* Open a database handle on the file to superlock. */
|
||||
rc = sqlite3_open_v2(
|
||||
zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
|
||||
);
|
||||
|
||||
/* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
|
||||
** a WAL database, this is all we need to do.
|
||||
**
|
||||
** A wrapper function is used to invoke the busy-handler instead of
|
||||
** registering the busy-handler function supplied by the user directly
|
||||
** with SQLite. This is because the same busy-handler function may be
|
||||
** invoked directly later on when attempting to obtain the extra locks
|
||||
** required in WAL mode. By using the wrapper, we are able to guarantee
|
||||
** that the "nBusy" integer parameter passed to the users busy-handler
|
||||
** represents the total number of busy-handler invocations made within
|
||||
** this call to sqlite3demo_superlock(), including any made during the
|
||||
** "BEGIN EXCLUSIVE".
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
busy.xBusy = xBusy;
|
||||
busy.pBusyArg = pBusyArg;
|
||||
sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy);
|
||||
rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0);
|
||||
}
|
||||
|
||||
/* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
|
||||
** database, call superlockWalLock() to obtain the extra locks required
|
||||
** to prevent readers, writers and/or checkpointers from accessing the
|
||||
** db while this process is holding the superlock.
|
||||
**
|
||||
** Before attempting any WAL locks, commit the transaction started above
|
||||
** to drop the WAL read and write locks currently held. Otherwise, the
|
||||
** new WAL locks may conflict with the old.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
|
||||
rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = superlockWalLock(pLock->db, &busy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3demo_superunlock(pLock);
|
||||
*ppLock = 0;
|
||||
}else{
|
||||
*ppLock = pLock;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** End of example code. Everything below here is the test harness.
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
#include <tcl.h>
|
||||
|
||||
struct InterpAndScript {
|
||||
Tcl_Interp *interp;
|
||||
Tcl_Obj *pScript;
|
||||
};
|
||||
typedef struct InterpAndScript InterpAndScript;
|
||||
|
||||
static void superunlock_del(ClientData cd){
|
||||
sqlite3demo_superunlock((void *)cd);
|
||||
}
|
||||
|
||||
static int superunlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int superlock_busy(void *pCtx, int nBusy){
|
||||
InterpAndScript *p = (InterpAndScript *)pCtx;
|
||||
Tcl_Obj *pEval; /* Script to evaluate */
|
||||
int iVal = 0; /* Value to return */
|
||||
|
||||
pEval = Tcl_DuplicateObj(p->pScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
|
||||
Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
||||
Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
|
||||
Tcl_DecrRefCount(pEval);
|
||||
|
||||
return iVal;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
|
||||
*/
|
||||
static int superlock_cmd(
|
||||
ClientData cd,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
void *pLock; /* Lock context */
|
||||
char *zPath;
|
||||
char *zVfs = 0;
|
||||
InterpAndScript busy = {0, 0};
|
||||
int (*xBusy)(void*,int) = 0; /* Busy handler callback */
|
||||
int rc; /* Return code from sqlite3demo_superlock() */
|
||||
|
||||
if( objc<3 || objc>5 ){
|
||||
Tcl_WrongNumArgs(
|
||||
interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
zPath = Tcl_GetString(objv[2]);
|
||||
|
||||
if( objc>3 ){
|
||||
zVfs = Tcl_GetString(objv[3]);
|
||||
if( strlen(zVfs)==0 ) zVfs = 0;
|
||||
}
|
||||
if( objc>4 ){
|
||||
busy.interp = interp;
|
||||
busy.pScript = objv[4];
|
||||
xBusy = superlock_busy;
|
||||
}
|
||||
|
||||
rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
|
||||
assert( rc==SQLITE_OK || pLock==0 );
|
||||
assert( rc!=SQLITE_OK || pLock!=0 );
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
extern const char *sqlite3ErrStr(int);
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
Tcl_CreateObjCommand(
|
||||
interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
|
||||
);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int SqliteSuperlock_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
+705
File diff suppressed because it is too large
Load Diff
+332
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
** 2006 June 13
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** Code for testing the virtual table interfaces. This code
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** The emphasis of this file is a virtual table that provides
|
||||
** access to TCL variables.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct tclvar_vtab tclvar_vtab;
|
||||
typedef struct tclvar_cursor tclvar_cursor;
|
||||
|
||||
/*
|
||||
** A tclvar virtual-table object
|
||||
*/
|
||||
struct tclvar_vtab {
|
||||
sqlite3_vtab base;
|
||||
Tcl_Interp *interp;
|
||||
};
|
||||
|
||||
/* A tclvar cursor object */
|
||||
struct tclvar_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
|
||||
Tcl_Obj *pList1; /* Result of [info vars ?pattern?] */
|
||||
Tcl_Obj *pList2; /* Result of [array names [lindex $pList1 $i1]] */
|
||||
int i1; /* Current item in pList1 */
|
||||
int i2; /* Current item (if any) in pList2 */
|
||||
};
|
||||
|
||||
/* Methods for the tclvar module */
|
||||
static int tclvarConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
tclvar_vtab *pVtab;
|
||||
static const char zSchema[] =
|
||||
"CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
|
||||
pVtab = sqlite3MallocZero( sizeof(*pVtab) );
|
||||
if( pVtab==0 ) return SQLITE_NOMEM;
|
||||
*ppVtab = &pVtab->base;
|
||||
pVtab->interp = (Tcl_Interp *)pAux;
|
||||
sqlite3_declare_vtab(db, zSchema);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* Note that for this virtual table, the xCreate and xConnect
|
||||
** methods are identical. */
|
||||
|
||||
static int tclvarDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
|
||||
/*
|
||||
** Open a new tclvar cursor.
|
||||
*/
|
||||
static int tclvarOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
tclvar_cursor *pCur;
|
||||
pCur = sqlite3MallocZero(sizeof(tclvar_cursor));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a tclvar cursor.
|
||||
*/
|
||||
static int tclvarClose(sqlite3_vtab_cursor *cur){
|
||||
tclvar_cursor *pCur = (tclvar_cursor *)cur;
|
||||
if( pCur->pList1 ){
|
||||
Tcl_DecrRefCount(pCur->pList1);
|
||||
}
|
||||
if( pCur->pList2 ){
|
||||
Tcl_DecrRefCount(pCur->pList2);
|
||||
}
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Returns 1 if data is ready, or 0 if not.
|
||||
*/
|
||||
static int next2(Tcl_Interp *interp, tclvar_cursor *pCur, Tcl_Obj *pObj){
|
||||
Tcl_Obj *p;
|
||||
|
||||
if( pObj ){
|
||||
if( !pCur->pList2 ){
|
||||
p = Tcl_NewStringObj("array names", -1);
|
||||
Tcl_IncrRefCount(p);
|
||||
Tcl_ListObjAppendElement(0, p, pObj);
|
||||
Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
|
||||
Tcl_DecrRefCount(p);
|
||||
pCur->pList2 = Tcl_GetObjResult(interp);
|
||||
Tcl_IncrRefCount(pCur->pList2);
|
||||
assert( pCur->i2==0 );
|
||||
}else{
|
||||
int n = 0;
|
||||
pCur->i2++;
|
||||
Tcl_ListObjLength(0, pCur->pList2, &n);
|
||||
if( pCur->i2>=n ){
|
||||
Tcl_DecrRefCount(pCur->pList2);
|
||||
pCur->pList2 = 0;
|
||||
pCur->i2 = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tclvarNext(sqlite3_vtab_cursor *cur){
|
||||
Tcl_Obj *pObj;
|
||||
int n = 0;
|
||||
int ok = 0;
|
||||
|
||||
tclvar_cursor *pCur = (tclvar_cursor *)cur;
|
||||
Tcl_Interp *interp = ((tclvar_vtab *)(cur->pVtab))->interp;
|
||||
|
||||
Tcl_ListObjLength(0, pCur->pList1, &n);
|
||||
while( !ok && pCur->i1<n ){
|
||||
Tcl_ListObjIndex(0, pCur->pList1, pCur->i1, &pObj);
|
||||
ok = next2(interp, pCur, pObj);
|
||||
if( !ok ){
|
||||
pCur->i1++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tclvarFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor;
|
||||
Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp;
|
||||
|
||||
Tcl_Obj *p = Tcl_NewStringObj("info vars", -1);
|
||||
Tcl_IncrRefCount(p);
|
||||
|
||||
assert( argc==0 || argc==1 );
|
||||
if( argc==1 ){
|
||||
Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1);
|
||||
Tcl_ListObjAppendElement(0, p, pArg);
|
||||
}
|
||||
Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
|
||||
if( pCur->pList1 ){
|
||||
Tcl_DecrRefCount(pCur->pList1);
|
||||
}
|
||||
if( pCur->pList2 ){
|
||||
Tcl_DecrRefCount(pCur->pList2);
|
||||
pCur->pList2 = 0;
|
||||
}
|
||||
pCur->i1 = 0;
|
||||
pCur->i2 = 0;
|
||||
pCur->pList1 = Tcl_GetObjResult(interp);
|
||||
Tcl_IncrRefCount(pCur->pList1);
|
||||
assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 );
|
||||
|
||||
Tcl_DecrRefCount(p);
|
||||
return tclvarNext(pVtabCursor);
|
||||
}
|
||||
|
||||
static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
Tcl_Obj *p1;
|
||||
Tcl_Obj *p2;
|
||||
const char *z1;
|
||||
const char *z2 = "";
|
||||
tclvar_cursor *pCur = (tclvar_cursor*)cur;
|
||||
Tcl_Interp *interp = ((tclvar_vtab *)cur->pVtab)->interp;
|
||||
|
||||
Tcl_ListObjIndex(interp, pCur->pList1, pCur->i1, &p1);
|
||||
Tcl_ListObjIndex(interp, pCur->pList2, pCur->i2, &p2);
|
||||
z1 = Tcl_GetString(p1);
|
||||
if( p2 ){
|
||||
z2 = Tcl_GetString(p2);
|
||||
}
|
||||
switch (i) {
|
||||
case 0: {
|
||||
sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
sqlite3_result_text(ctx, z2, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
|
||||
sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
*pRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int tclvarEof(sqlite3_vtab_cursor *cur){
|
||||
tclvar_cursor *pCur = (tclvar_cursor*)cur;
|
||||
return (pCur->pList2?0:1);
|
||||
}
|
||||
|
||||
static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int ii;
|
||||
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
if( pCons->iColumn==0 && pCons->usable
|
||||
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
struct sqlite3_index_constraint_usage *pUsage;
|
||||
pUsage = &pIdxInfo->aConstraintUsage[ii];
|
||||
pUsage->omit = 0;
|
||||
pUsage->argvIndex = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
|
||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
|
||||
if( pCons->iColumn==0 && pCons->usable
|
||||
&& pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||||
struct sqlite3_index_constraint_usage *pUsage;
|
||||
pUsage = &pIdxInfo->aConstraintUsage[ii];
|
||||
pUsage->omit = 1;
|
||||
pUsage->argvIndex = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module tclvarModule = {
|
||||
0, /* iVersion */
|
||||
tclvarConnect,
|
||||
tclvarConnect,
|
||||
tclvarBestIndex,
|
||||
tclvarDisconnect,
|
||||
tclvarDisconnect,
|
||||
tclvarOpen, /* xOpen - open a cursor */
|
||||
tclvarClose, /* xClose - close a cursor */
|
||||
tclvarFilter, /* xFilter - configure scan constraints */
|
||||
tclvarNext, /* xNext - advance a cursor */
|
||||
tclvarEof, /* xEof - check for end of scan */
|
||||
tclvarColumn, /* xColumn - read data */
|
||||
tclvarRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
};
|
||||
|
||||
/*
|
||||
** Decode a pointer to an sqlite3 object.
|
||||
*/
|
||||
extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
|
||||
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
static int register_tclvar_module(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
*/
|
||||
int Sqlitetesttclvar_Init(Tcl_Interp *interp){
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
static struct {
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
{ "register_tclvar_module", register_tclvar_module, 0 },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
|
||||
aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
|
||||
}
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
+647
File diff suppressed because it is too large
Load Diff
+1510
File diff suppressed because it is too large
Load Diff
+887
File diff suppressed because it is too large
Load Diff
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
** 2008 September 1
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** The code in this file contains sample implementations of the
|
||||
** sqlite3_wsd_init() and sqlite3_wsd_find() functions required if the
|
||||
** SQLITE_OMIT_WSD symbol is defined at build time.
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_OMIT_WSD) && defined(SQLITE_TEST)
|
||||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#define PLS_HASHSIZE 43
|
||||
|
||||
typedef struct ProcessLocalStorage ProcessLocalStorage;
|
||||
typedef struct ProcessLocalVar ProcessLocalVar;
|
||||
|
||||
struct ProcessLocalStorage {
|
||||
ProcessLocalVar *aData[PLS_HASHSIZE];
|
||||
int nFree;
|
||||
u8 *pFree;
|
||||
};
|
||||
|
||||
struct ProcessLocalVar {
|
||||
void *pKey;
|
||||
ProcessLocalVar *pNext;
|
||||
};
|
||||
|
||||
static ProcessLocalStorage *pGlobal = 0;
|
||||
|
||||
int sqlite3_wsd_init(int N, int J){
|
||||
if( !pGlobal ){
|
||||
int nMalloc = N + sizeof(ProcessLocalStorage) + J*sizeof(ProcessLocalVar);
|
||||
pGlobal = (ProcessLocalStorage *)malloc(nMalloc);
|
||||
if( pGlobal ){
|
||||
memset(pGlobal, 0, sizeof(ProcessLocalStorage));
|
||||
pGlobal->nFree = nMalloc - sizeof(ProcessLocalStorage);
|
||||
pGlobal->pFree = (u8 *)&pGlobal[1];
|
||||
}
|
||||
}
|
||||
|
||||
return pGlobal ? SQLITE_OK : SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
void *sqlite3_wsd_find(void *K, int L){
|
||||
int i;
|
||||
int iHash = 0;
|
||||
ProcessLocalVar *pVar;
|
||||
|
||||
/* Calculate a hash of K */
|
||||
for(i=0; i<sizeof(void*); i++){
|
||||
iHash = (iHash<<3) + ((unsigned char *)&K)[i];
|
||||
}
|
||||
iHash = iHash%PLS_HASHSIZE;
|
||||
|
||||
/* Search the hash table for K. */
|
||||
for(pVar=pGlobal->aData[iHash]; pVar && pVar->pKey!=K; pVar=pVar->pNext);
|
||||
|
||||
/* If no entry for K was found, create and populate a new one. */
|
||||
if( !pVar ){
|
||||
int nByte = ROUND8(sizeof(ProcessLocalVar) + L);
|
||||
assert( pGlobal->nFree>=nByte );
|
||||
pVar = (ProcessLocalVar *)pGlobal->pFree;
|
||||
pVar->pKey = K;
|
||||
pVar->pNext = pGlobal->aData[iHash];
|
||||
pGlobal->aData[iHash] = pVar;
|
||||
pGlobal->nFree -= nByte;
|
||||
pGlobal->pFree += nByte;
|
||||
memcpy(&pVar[1], K, L);
|
||||
}
|
||||
|
||||
return (void *)&pVar[1];
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user