Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ab6b168f8 |
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
** 2001 September 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 is the implementation of generic hash-tables used in SQLite.
|
||||
** We've modified it slightly to serve as a standalone hash table
|
||||
** implementation for the full-text indexing module.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ft_hash.h"
|
||||
|
||||
void *malloc_and_zero(int n){
|
||||
void *p = malloc(n);
|
||||
if( p ){
|
||||
memset(p, 0, n);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Turn bulk memory into a hash table object by initializing the
|
||||
** fields of the Hash structure.
|
||||
**
|
||||
** "pNew" is a pointer to the hash table that is to be initialized.
|
||||
** keyClass is one of the constants HASH_INT, HASH_POINTER,
|
||||
** HASH_BINARY, or HASH_STRING. The value of keyClass
|
||||
** determines what kind of key the hash table will use. "copyKey" is
|
||||
** true if the hash table should make its own private copy of keys and
|
||||
** false if it should just use the supplied pointer. CopyKey only makes
|
||||
** sense for HASH_STRING and HASH_BINARY and is ignored
|
||||
** for other key classes.
|
||||
*/
|
||||
void HashInit(Hash *pNew, int keyClass, int copyKey){
|
||||
assert( pNew!=0 );
|
||||
assert( keyClass>=HASH_STRING && keyClass<=HASH_BINARY );
|
||||
pNew->keyClass = keyClass;
|
||||
#if 0
|
||||
if( keyClass==HASH_POINTER || keyClass==HASH_INT ) copyKey = 0;
|
||||
#endif
|
||||
pNew->copyKey = copyKey;
|
||||
pNew->first = 0;
|
||||
pNew->count = 0;
|
||||
pNew->htsize = 0;
|
||||
pNew->ht = 0;
|
||||
pNew->xMalloc = malloc_and_zero;
|
||||
pNew->xFree = free;
|
||||
}
|
||||
|
||||
/* Remove all entries from a hash table. Reclaim all memory.
|
||||
** Call this routine to delete a hash table or to reset a hash table
|
||||
** to the empty state.
|
||||
*/
|
||||
void HashClear(Hash *pH){
|
||||
HashElem *elem; /* For looping over all elements of the table */
|
||||
|
||||
assert( pH!=0 );
|
||||
elem = pH->first;
|
||||
pH->first = 0;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = 0;
|
||||
pH->htsize = 0;
|
||||
while( elem ){
|
||||
HashElem *next_elem = elem->next;
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree(elem);
|
||||
elem = next_elem;
|
||||
}
|
||||
pH->count = 0;
|
||||
}
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Hash and comparison functions when the mode is HASH_INT
|
||||
*/
|
||||
static int intHash(const void *pKey, int nKey){
|
||||
return nKey ^ (nKey<<8) ^ (nKey>>8);
|
||||
}
|
||||
static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
return n2 - n1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Hash and comparison functions when the mode is HASH_POINTER
|
||||
*/
|
||||
static int ptrHash(const void *pKey, int nKey){
|
||||
uptr x = Addr(pKey);
|
||||
return x ^ (x<<8) ^ (x>>8);
|
||||
}
|
||||
static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( pKey1==pKey2 ) return 0;
|
||||
if( pKey1<pKey2 ) return -1;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is HASH_STRING
|
||||
*/
|
||||
static int strHash(const void *pKey, int nKey){
|
||||
const char *z = (const char *)pKey;
|
||||
int h = 0;
|
||||
if( nKey<=0 ) nKey = (int) strlen(z);
|
||||
while( nKey > 0 ){
|
||||
h = (h<<3) ^ h ^ *z++;
|
||||
nKey--;
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return strncmp((const char*)pKey1,(const char*)pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is HASH_BINARY
|
||||
*/
|
||||
static int binHash(const void *pKey, int nKey){
|
||||
int h = 0;
|
||||
const char *z = (const char *)pKey;
|
||||
while( nKey-- > 0 ){
|
||||
h = (h<<3) ^ h ^ *(z++);
|
||||
}
|
||||
return h & 0x7fffffff;
|
||||
}
|
||||
static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( n1!=n2 ) return 1;
|
||||
return memcmp(pKey1,pKey2,n1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** The C syntax in this function definition may be unfamilar to some
|
||||
** programmers, so we provide the following additional explanation:
|
||||
**
|
||||
** The name of the function is "hashFunction". The function takes a
|
||||
** single parameter "keyClass". The return value of hashFunction()
|
||||
** is a pointer to another function. Specifically, the return value
|
||||
** of hashFunction() is a pointer to a function that takes two parameters
|
||||
** with types "const void*" and "int" and returns an "int".
|
||||
*/
|
||||
static int (*hashFunction(int keyClass))(const void*,int){
|
||||
#if 0 /* HASH_INT and HASH_POINTER are never used */
|
||||
switch( keyClass ){
|
||||
case HASH_INT: return &intHash;
|
||||
case HASH_POINTER: return &ptrHash;
|
||||
case HASH_STRING: return &strHash;
|
||||
case HASH_BINARY: return &binHash;;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if( keyClass==HASH_STRING ){
|
||||
return &strHash;
|
||||
}else{
|
||||
assert( keyClass==HASH_BINARY );
|
||||
return &binHash;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the appropriate hash function given the key class.
|
||||
**
|
||||
** For help in interpreted the obscure C code in the function definition,
|
||||
** see the header comment on the previous function.
|
||||
*/
|
||||
static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
|
||||
#if 0 /* HASH_INT and HASH_POINTER are never used */
|
||||
switch( keyClass ){
|
||||
case HASH_INT: return &intCompare;
|
||||
case HASH_POINTER: return &ptrCompare;
|
||||
case HASH_STRING: return &strCompare;
|
||||
case HASH_BINARY: return &binCompare;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
if( keyClass==HASH_STRING ){
|
||||
return &strCompare;
|
||||
}else{
|
||||
assert( keyClass==HASH_BINARY );
|
||||
return &binCompare;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Link an element into the hash table
|
||||
*/
|
||||
static void insertElement(
|
||||
Hash *pH, /* The complete hash table */
|
||||
struct _ht *pEntry, /* The entry into which pNew is inserted */
|
||||
HashElem *pNew /* The element to be inserted */
|
||||
){
|
||||
HashElem *pHead; /* First element already in pEntry */
|
||||
pHead = pEntry->chain;
|
||||
if( pHead ){
|
||||
pNew->next = pHead;
|
||||
pNew->prev = pHead->prev;
|
||||
if( pHead->prev ){ pHead->prev->next = pNew; }
|
||||
else { pH->first = pNew; }
|
||||
pHead->prev = pNew;
|
||||
}else{
|
||||
pNew->next = pH->first;
|
||||
if( pH->first ){ pH->first->prev = pNew; }
|
||||
pNew->prev = 0;
|
||||
pH->first = pNew;
|
||||
}
|
||||
pEntry->count++;
|
||||
pEntry->chain = pNew;
|
||||
}
|
||||
|
||||
|
||||
/* Resize the hash table so that it cantains "new_size" buckets.
|
||||
** "new_size" must be a power of 2. The hash table might fail
|
||||
** to resize if sqliteMalloc() fails.
|
||||
*/
|
||||
static void rehash(Hash *pH, int new_size){
|
||||
struct _ht *new_ht; /* The new hash table */
|
||||
HashElem *elem, *next_elem; /* For looping over existing elements */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( (new_size & (new_size-1))==0 );
|
||||
new_ht = (struct _ht *)pH->xMalloc( new_size*sizeof(struct _ht) );
|
||||
if( new_ht==0 ) return;
|
||||
if( pH->ht ) pH->xFree(pH->ht);
|
||||
pH->ht = new_ht;
|
||||
pH->htsize = new_size;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
|
||||
int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
|
||||
next_elem = elem->next;
|
||||
insertElement(pH, &new_ht[h], elem);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function (for internal use only) locates an element in an
|
||||
** hash table that matches the given key. The hash for this key has
|
||||
** already been computed and is passed as the 4th parameter.
|
||||
*/
|
||||
static HashElem *findElementGivenHash(
|
||||
const Hash *pH, /* The pH to be searched */
|
||||
const void *pKey, /* The key we are searching for */
|
||||
int nKey,
|
||||
int h /* The hash for this key. */
|
||||
){
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
int count; /* Number of elements left to test */
|
||||
int (*xCompare)(const void*,int,const void*,int); /* comparison function */
|
||||
|
||||
if( pH->ht ){
|
||||
struct _ht *pEntry = &pH->ht[h];
|
||||
elem = pEntry->chain;
|
||||
count = pEntry->count;
|
||||
xCompare = compareFunction(pH->keyClass);
|
||||
while( count-- && elem ){
|
||||
if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
|
||||
return elem;
|
||||
}
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a single entry from the hash table given a pointer to that
|
||||
** element and a hash on the element's key.
|
||||
*/
|
||||
static void removeElementGivenHash(
|
||||
Hash *pH, /* The pH containing "elem" */
|
||||
HashElem* elem, /* The element to be removed from the pH */
|
||||
int h /* Hash value for the element */
|
||||
){
|
||||
struct _ht *pEntry;
|
||||
if( elem->prev ){
|
||||
elem->prev->next = elem->next;
|
||||
}else{
|
||||
pH->first = elem->next;
|
||||
}
|
||||
if( elem->next ){
|
||||
elem->next->prev = elem->prev;
|
||||
}
|
||||
pEntry = &pH->ht[h];
|
||||
if( pEntry->chain==elem ){
|
||||
pEntry->chain = elem->next;
|
||||
}
|
||||
pEntry->count--;
|
||||
if( pEntry->count<=0 ){
|
||||
pEntry->chain = 0;
|
||||
}
|
||||
if( pH->copyKey && elem->pKey ){
|
||||
pH->xFree(elem->pKey);
|
||||
}
|
||||
pH->xFree( elem );
|
||||
pH->count--;
|
||||
if( pH->count<=0 ){
|
||||
assert( pH->first==0 );
|
||||
assert( pH->count==0 );
|
||||
HashClear(pH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to locate an element of the hash table pH with a key
|
||||
** that matches pKey,nKey. Return the data for this element if it is
|
||||
** found, or NULL if there is no match.
|
||||
*/
|
||||
void *HashFind(const Hash *pH, const void *pKey, int nKey){
|
||||
int h; /* A hash on key */
|
||||
HashElem *elem; /* The element that matches key */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
if( pH==0 || pH->ht==0 ) return 0;
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
h = (*xHash)(pKey,nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
|
||||
return elem ? elem->data : 0;
|
||||
}
|
||||
|
||||
/* Insert an element into the hash table pH. The key is pKey,nKey
|
||||
** and the data is "data".
|
||||
**
|
||||
** If no element exists with a matching key, then a new
|
||||
** element is created. A copy of the key is made if the copyKey
|
||||
** flag is set. NULL is returned.
|
||||
**
|
||||
** If another element already exists with the same key, then the
|
||||
** new data replaces the old data and the old data is returned.
|
||||
** The key is not copied in this instance. If a malloc fails, then
|
||||
** the new data is returned and the hash table is unchanged.
|
||||
**
|
||||
** If the "data" parameter to this function is NULL, then the
|
||||
** element corresponding to "key" is removed from the hash table.
|
||||
*/
|
||||
void *HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
|
||||
int hraw; /* Raw hash value of the key */
|
||||
int h; /* the hash of the key modulo hash table size */
|
||||
HashElem *elem; /* Used to loop thru the element list */
|
||||
HashElem *new_elem; /* New element added to the pH */
|
||||
int (*xHash)(const void*,int); /* The hash function */
|
||||
|
||||
assert( pH!=0 );
|
||||
xHash = hashFunction(pH->keyClass);
|
||||
assert( xHash!=0 );
|
||||
hraw = (*xHash)(pKey, nKey);
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
elem = findElementGivenHash(pH,pKey,nKey,h);
|
||||
if( elem ){
|
||||
void *old_data = elem->data;
|
||||
if( data==0 ){
|
||||
removeElementGivenHash(pH,elem,h);
|
||||
}else{
|
||||
elem->data = data;
|
||||
}
|
||||
return old_data;
|
||||
}
|
||||
if( data==0 ) return 0;
|
||||
new_elem = (HashElem*)pH->xMalloc( sizeof(HashElem) );
|
||||
if( new_elem==0 ) return data;
|
||||
if( pH->copyKey && pKey!=0 ){
|
||||
new_elem->pKey = pH->xMalloc( nKey );
|
||||
if( new_elem->pKey==0 ){
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
memcpy((void*)new_elem->pKey, pKey, nKey);
|
||||
}else{
|
||||
new_elem->pKey = (void*)pKey;
|
||||
}
|
||||
new_elem->nKey = nKey;
|
||||
pH->count++;
|
||||
if( pH->htsize==0 ){
|
||||
rehash(pH,8);
|
||||
if( pH->htsize==0 ){
|
||||
pH->count = 0;
|
||||
pH->xFree(new_elem);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if( pH->count > pH->htsize ){
|
||||
rehash(pH,pH->htsize*2);
|
||||
}
|
||||
assert( pH->htsize>0 );
|
||||
assert( (pH->htsize & (pH->htsize-1))==0 );
|
||||
h = hraw & (pH->htsize-1);
|
||||
insertElement(pH, &pH->ht[h], new_elem);
|
||||
new_elem->data = data;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
** 2001 September 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 is the header file for the generic hash-table implemenation
|
||||
** used in SQLite. We've modified it slightly to serve as a standalone
|
||||
** hash table implementation for the full-text indexing module.
|
||||
**
|
||||
*/
|
||||
#ifndef _HASH_H_
|
||||
#define _HASH_H_
|
||||
|
||||
/* Forward declarations of structures. */
|
||||
typedef struct Hash Hash;
|
||||
typedef struct HashElem HashElem;
|
||||
|
||||
/* A complete hash table is an instance of the following structure.
|
||||
** The internals of this structure are intended to be opaque -- client
|
||||
** code should not attempt to access or modify the fields of this structure
|
||||
** directly. Change this structure only by using the routines below.
|
||||
** However, many of the "procedures" and "functions" for modifying and
|
||||
** accessing this structure are really macros, so we can't really make
|
||||
** this structure opaque.
|
||||
*/
|
||||
struct Hash {
|
||||
char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */
|
||||
char copyKey; /* True if copy of key made on insert */
|
||||
int count; /* Number of entries in this table */
|
||||
HashElem *first; /* The first element of the array */
|
||||
void *(*xMalloc)(int); /* malloc() function to use */
|
||||
void (*xFree)(void *); /* free() function to use */
|
||||
int htsize; /* Number of buckets in the hash table */
|
||||
struct _ht { /* the hash table */
|
||||
int count; /* Number of entries with this hash */
|
||||
HashElem *chain; /* Pointer to first entry with this hash */
|
||||
} *ht;
|
||||
};
|
||||
|
||||
/* Each element in the hash table is an instance of the following
|
||||
** structure. All elements are stored on a single doubly-linked list.
|
||||
**
|
||||
** Again, this structure is intended to be opaque, but it can't really
|
||||
** be opaque because it is used by macros.
|
||||
*/
|
||||
struct HashElem {
|
||||
HashElem *next, *prev; /* Next and previous elements in the table */
|
||||
void *data; /* Data associated with this element */
|
||||
void *pKey; int nKey; /* Key associated with this element */
|
||||
};
|
||||
|
||||
/*
|
||||
** There are 4 different modes of operation for a hash table:
|
||||
**
|
||||
** HASH_INT nKey is used as the key and pKey is ignored.
|
||||
**
|
||||
** HASH_POINTER pKey is used as the key and nKey is ignored.
|
||||
**
|
||||
** HASH_STRING pKey points to a string that is nKey bytes long
|
||||
** (including the null-terminator, if any). Case
|
||||
** is respected in comparisons.
|
||||
**
|
||||
** HASH_BINARY pKey points to binary data nKey bytes long.
|
||||
** memcmp() is used to compare keys.
|
||||
**
|
||||
** A copy of the key is made for HASH_STRING and HASH_BINARY
|
||||
** if the copyKey parameter to HashInit is 1.
|
||||
*/
|
||||
/* #define HASH_INT 1 // NOT USED */
|
||||
/* #define HASH_POINTER 2 // NOT USED */
|
||||
#define HASH_STRING 3
|
||||
#define HASH_BINARY 4
|
||||
|
||||
/*
|
||||
** Access routines. To delete, insert a NULL pointer.
|
||||
*/
|
||||
void HashInit(Hash*, int keytype, int copyKey);
|
||||
void *HashInsert(Hash*, const void *pKey, int nKey, void *pData);
|
||||
void *HashFind(const Hash*, const void *pKey, int nKey);
|
||||
void HashClear(Hash*);
|
||||
|
||||
/*
|
||||
** Macros for looping over all elements of a hash table. The idiom is
|
||||
** like this:
|
||||
**
|
||||
** Hash h;
|
||||
** HashElem *p;
|
||||
** ...
|
||||
** for(p=HashFirst(&h); p; p=HashNext(p)){
|
||||
** SomeStructure *pData = HashData(p);
|
||||
** // do something with pData
|
||||
** }
|
||||
*/
|
||||
#define HashFirst(H) ((H)->first)
|
||||
#define HashNext(E) ((E)->next)
|
||||
#define HashData(E) ((E)->data)
|
||||
#define HashKey(E) ((E)->pKey)
|
||||
#define HashKeysize(E) ((E)->nKey)
|
||||
|
||||
/*
|
||||
** Number of entries in a hash table
|
||||
*/
|
||||
#define HashCount(H) ((H)->count)
|
||||
|
||||
#endif /* _HASH_H_ */
|
||||
-1496
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int fulltext_init(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Implementation of the "simple" full-text-search tokenizer.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__APPLE__)
|
||||
#include <malloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "tokenizer.h"
|
||||
|
||||
/* Duplicate a string; the caller must free() the returned string.
|
||||
* (We don't use strdup() since it's not part of the standard C library and
|
||||
* may not be available everywhere.) */
|
||||
/* TODO(shess) Copied from fulltext.c, consider util.c for such
|
||||
** things. */
|
||||
static char *string_dup(const char *s){
|
||||
char *str = malloc(strlen(s) + 1);
|
||||
strcpy(str, s);
|
||||
return str;
|
||||
}
|
||||
|
||||
typedef struct simple_tokenizer {
|
||||
sqlite3_tokenizer base;
|
||||
const char *zDelim; /* token delimiters */
|
||||
} simple_tokenizer;
|
||||
|
||||
typedef struct simple_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
const char *pInput; /* input we are tokenizing */
|
||||
int nBytes; /* size of the input */
|
||||
const char *pCurrent; /* current position in pInput */
|
||||
int iToken; /* index of next token to be returned */
|
||||
char *zToken; /* storage for current token */
|
||||
int nTokenBytes; /* actual size of current token */
|
||||
int nTokenAllocated; /* space allocated to zToken buffer */
|
||||
} simple_tokenizer_cursor;
|
||||
|
||||
static sqlite3_tokenizer_module simpleTokenizerModule;/* forward declaration */
|
||||
|
||||
static int simpleCreate(
|
||||
int argc, const char **argv,
|
||||
sqlite3_tokenizer **ppTokenizer
|
||||
){
|
||||
simple_tokenizer *t;
|
||||
|
||||
t = (simple_tokenizer *) malloc(sizeof(simple_tokenizer));
|
||||
/* TODO(shess) Delimiters need to remain the same from run to run,
|
||||
** else we need to reindex. One solution would be a meta-table to
|
||||
** track such information in the database, then we'd only want this
|
||||
** information on the initial create.
|
||||
*/
|
||||
if( argc>1 ){
|
||||
t->zDelim = string_dup(argv[1]);
|
||||
} else {
|
||||
/* Build a string excluding alphanumeric ASCII characters */
|
||||
char zDelim[0x80]; /* nul-terminated, so nul not a member */
|
||||
int i, j;
|
||||
for(i=1, j=0; i<0x80; i++){
|
||||
if( !isalnum(i) ){
|
||||
zDelim[j++] = i;
|
||||
}
|
||||
}
|
||||
zDelim[j++] = '\0';
|
||||
assert( j<=sizeof(zDelim) );
|
||||
t->zDelim = string_dup(zDelim);
|
||||
}
|
||||
|
||||
*ppTokenizer = &t->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
|
||||
simple_tokenizer *t = (simple_tokenizer *) pTokenizer;
|
||||
|
||||
free((void *) t->zDelim);
|
||||
free(t);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int simpleOpen(
|
||||
sqlite3_tokenizer *pTokenizer,
|
||||
const char *pInput, int nBytes,
|
||||
sqlite3_tokenizer_cursor **ppCursor
|
||||
){
|
||||
simple_tokenizer_cursor *c;
|
||||
|
||||
c = (simple_tokenizer_cursor *) malloc(sizeof(simple_tokenizer_cursor));
|
||||
c->pInput = pInput;
|
||||
c->nBytes = nBytes<0 ? (int) strlen(pInput) : nBytes;
|
||||
c->pCurrent = c->pInput; /* start tokenizing at the beginning */
|
||||
c->iToken = 0;
|
||||
c->zToken = NULL; /* no space allocated, yet. */
|
||||
c->nTokenBytes = 0;
|
||||
c->nTokenAllocated = 0;
|
||||
|
||||
*ppCursor = &c->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int simpleClose(sqlite3_tokenizer_cursor *pCursor){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
|
||||
if( NULL!=c->zToken ){
|
||||
free(c->zToken);
|
||||
}
|
||||
free(c);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int simpleNext(
|
||||
sqlite3_tokenizer_cursor *pCursor,
|
||||
const char **ppToken, int *pnBytes,
|
||||
int *piStartOffset, int *piEndOffset, int *piPosition
|
||||
){
|
||||
simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor;
|
||||
simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer;
|
||||
int ii;
|
||||
|
||||
while( c->pCurrent-c->pInput<c->nBytes ){
|
||||
int n = (int) strcspn(c->pCurrent, t->zDelim);
|
||||
if( n>0 ){
|
||||
if( n+1>c->nTokenAllocated ){
|
||||
c->zToken = realloc(c->zToken, n+1);
|
||||
}
|
||||
for(ii=0; ii<n; ii++){
|
||||
/* TODO(shess) This needs expansion to handle UTF-8
|
||||
** case-insensitivity.
|
||||
*/
|
||||
char ch = c->pCurrent[ii];
|
||||
c->zToken[ii] = (unsigned char)ch<0x80 ? tolower(ch) : ch;
|
||||
}
|
||||
c->zToken[n] = '\0';
|
||||
*ppToken = c->zToken;
|
||||
*pnBytes = n;
|
||||
*piStartOffset = (int) (c->pCurrent-c->pInput);
|
||||
*piEndOffset = *piStartOffset+n;
|
||||
*piPosition = c->iToken++;
|
||||
c->pCurrent += n + 1;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
c->pCurrent += n + 1;
|
||||
/* TODO(shess) could strspn() to skip delimiters en masse. Needs
|
||||
** to happen in two places, though, which is annoying.
|
||||
*/
|
||||
}
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
|
||||
static sqlite3_tokenizer_module simpleTokenizerModule = {
|
||||
0,
|
||||
simpleCreate,
|
||||
simpleDestroy,
|
||||
simpleOpen,
|
||||
simpleClose,
|
||||
simpleNext,
|
||||
};
|
||||
|
||||
void get_simple_tokenizer_module(
|
||||
sqlite3_tokenizer_module **ppModule
|
||||
){
|
||||
*ppModule = &simpleTokenizerModule;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
** 2006 July 10
|
||||
**
|
||||
** The author disclaims copyright to this source code.
|
||||
**
|
||||
*************************************************************************
|
||||
** Defines the interface to tokenizers used by fulltext-search. There
|
||||
** are three basic components:
|
||||
**
|
||||
** sqlite3_tokenizer_module is a singleton defining the tokenizer
|
||||
** interface functions. This is essentially the class structure for
|
||||
** tokenizers.
|
||||
**
|
||||
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
|
||||
** including customization information defined at creation time.
|
||||
**
|
||||
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
|
||||
** tokens from a particular input.
|
||||
*/
|
||||
#ifndef _TOKENIZER_H_
|
||||
#define _TOKENIZER_H_
|
||||
|
||||
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
|
||||
** If tokenizers are to be allowed to call sqlite3_*() functions, then
|
||||
** we will need a way to register the API consistently.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Structures used by the tokenizer interface.
|
||||
*/
|
||||
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
|
||||
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
|
||||
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
|
||||
|
||||
struct sqlite3_tokenizer_module {
|
||||
int iVersion; /* currently 0 */
|
||||
|
||||
/*
|
||||
** Create and destroy a tokenizer. argc/argv are passed down from
|
||||
** the fulltext virtual table creation to allow customization.
|
||||
*/
|
||||
int (*xCreate)(int argc, const char **argv,
|
||||
sqlite3_tokenizer **ppTokenizer);
|
||||
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
|
||||
|
||||
/*
|
||||
** Tokenize a particular input. Call xOpen() to prepare to
|
||||
** tokenize, xNext() repeatedly until it returns SQLITE_DONE, then
|
||||
** xClose() to free any internal state. The pInput passed to
|
||||
** xOpen() must exist until the cursor is closed. The ppToken
|
||||
** result from xNext() is only valid until the next call to xNext()
|
||||
** or until xClose() is called.
|
||||
*/
|
||||
/* TODO(shess) current implementation requires pInput to be
|
||||
** nul-terminated. This should either be fixed, or pInput/nBytes
|
||||
** should be converted to zInput.
|
||||
*/
|
||||
int (*xOpen)(sqlite3_tokenizer *pTokenizer,
|
||||
const char *pInput, int nBytes,
|
||||
sqlite3_tokenizer_cursor **ppCursor);
|
||||
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
|
||||
int (*xNext)(sqlite3_tokenizer_cursor *pCursor,
|
||||
const char **ppToken, int *pnBytes,
|
||||
int *piStartOffset, int *piEndOffset, int *piPosition);
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer {
|
||||
sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
struct sqlite3_tokenizer_cursor {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
|
||||
/* Tokenizer implementations will typically add additional fields */
|
||||
};
|
||||
|
||||
/*
|
||||
** Get the module for a tokenizer which generates tokens based on a
|
||||
** set of non-token characters. The default is to break tokens at any
|
||||
** non-alnum character, though the set of delimiters can also be
|
||||
** specified by the first argv argument to xCreate().
|
||||
*/
|
||||
/* TODO(shess) This doesn't belong here. Need some sort of
|
||||
** registration process.
|
||||
*/
|
||||
void get_simple_tokenizer_module(sqlite3_tokenizer_module **ppModule);
|
||||
|
||||
#endif /* _TOKENIZER_H_ */
|
||||
@@ -1,5 +1,5 @@
|
||||
C Fix\sa\sfile\sdescriptor\sleak\sin\sthe\sexclusive2\stest\sscript.\s(CVS\s3871)
|
||||
D 2007-04-25T12:06:59
|
||||
C Back-port\scritical\ssoft\sheap\slimit\sfixes\sto\sversion\s3.3.17.\s\sThe\sfollowing\ncheck-ins\swere\sback-ported:\s(4031),\s(4037),\s(4097),\s(4202),\s(4203),\n(4207),\s(4208),\s(4214).\s(CVS\s4222)
|
||||
D 2007-08-14T13:20:27
|
||||
F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62
|
||||
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
|
||||
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
||||
@@ -20,8 +20,6 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
|
||||
F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
|
||||
F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1
|
||||
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
|
||||
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
|
||||
F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5
|
||||
F ext/fts1/fts1.c 1ee986c31a7080d509602fa182738c5a8862fbff
|
||||
F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6
|
||||
F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114
|
||||
@@ -29,10 +27,6 @@ F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089
|
||||
F ext/fts1/fts1_porter.c 263e6a89c8769a456152e1abcd8821fcb4699e5b
|
||||
F ext/fts1/fts1_tokenizer.h fdea722c38a9f82ed921642981234f666e47919c
|
||||
F ext/fts1/fts1_tokenizer1.c df09e638156abfe320e705c839baa98d595f45a7
|
||||
F ext/fts1/fulltext.c d935e600d87bc86b7d64f55c7520ea41d6034c5c
|
||||
F ext/fts1/fulltext.h 08525a47852d1d62a0be81d3fc3fe2d23b094efd
|
||||
F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d
|
||||
F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9
|
||||
F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts2/fts2.c dd35df80f4b99181d610302c5a4ac71406a02fe0
|
||||
F ext/fts2/fts2.h 591916a822cfb6426518fdbf6069359119bc46eb
|
||||
@@ -53,20 +47,18 @@ F publish.sh 8ef26af8417ddd03d471779e9c14764b3abe7706
|
||||
F spec.template b2f6c4e488cbc3b993a57deba22cbc36203c4da3
|
||||
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.def a96c1d0d39362b763d2ddba220a32da41a15c4b4
|
||||
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
||||
F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3
|
||||
F src/analyze.c 4bbf5ddf9680587c6d4917e02e378b6037be3651
|
||||
F src/attach.c a16ada4a4654a0d126b8223ec9494ebb81bc5c3c
|
||||
F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
|
||||
F src/btree.c 960bf64baa4d2bdb96019698e60d0b7763bf4e7e
|
||||
F src/btree.c 4b987e7a6b0c0ef8c0d7805e4c37481b0e40c177
|
||||
F src/btree.h 9b2cc0d113c0bc2d37d244b9a394d56948c9acbf
|
||||
F src/build.c 1880da163d9aa404016242b8b76d69907f682cd8
|
||||
F src/callback.c 6414ed32d55859d0f65067aa5b88d2da27b3af9e
|
||||
F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
|
||||
F src/date.c 74b76691bddf58b634f6bf4a77c8c58234268c6e
|
||||
F src/delete.c 5c0d89b3ef7d48fe1f5124bfe8341f982747fe29
|
||||
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
|
||||
F src/expr.c 2f0f9f89efe9170e5e6ca5d5e93a9d5896fff5ac
|
||||
F src/func.c 007d957c057bb42b0d37aa6ad4be0e1c67a8871b
|
||||
F src/hash.c 67b23e14f0257b69a3e8aa663e4eeadc1a2b6fd5
|
||||
@@ -75,19 +67,14 @@ F src/insert.c 413cc06990cb3c401e64e596776c1e43934f8841
|
||||
F src/legacy.c c05a599a37f703ed1e66fdb5df60c2db65f29e71
|
||||
F src/loadext.c afe4f4755dc49c36ef505748bbdddecb9f1d02a2
|
||||
F src/main.c e6eb036c3580ba9116fedfe4a8b58ed63d5abb37
|
||||
F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
|
||||
F src/os.c 4650e98aadd27abfe1698ff58edf6893c58d4881
|
||||
F src/os.h 9240adf088fd55732f8926f9017601ba3430ead8
|
||||
F src/os_common.h a38233cd3b1f260db6f01f1093295d5708130065
|
||||
F src/os_os2.c 2ce97909b926a598823f97338027dbec1dcf4165
|
||||
F src/os_os2.h e5f17dd69333632bbc3112881ea407c37d245eb3
|
||||
F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
|
||||
F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
|
||||
F src/os_unix.c 426b4c03c304ad78746d65d9ba101e0b72e18e23
|
||||
F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
|
||||
F src/os_win.c e94903c7dc1c0599c8ddce42efa0b6928068ddc5
|
||||
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
|
||||
F src/pager.c 33c632ce9c228d87f14879a139fa123d43e4bf25
|
||||
F src/pager.c f4bc6263966be7c11c9f2fd3940cb438368923d9
|
||||
F src/pager.h d652ddf092d2318d00e41f8539760fe8e57c157c
|
||||
F src/parse.y b6cfbadb6d5b21b5087d30698ee5af0ebb098767
|
||||
F src/pragma.c 3b992b5b2640d6ae25cef05aa6a42cd1d6c43234
|
||||
@@ -95,7 +82,6 @@ F src/prepare.c 4cb9c9eb926e8baf5652ca4b4f2416f53f5b5370
|
||||
F src/printf.c 0c6f40648770831341ac45ab32423a80b4c87f05
|
||||
F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88
|
||||
F src/select.c b914abca0ba28893e7fb7c7fb97a05e240e2ce8b
|
||||
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
|
||||
F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
|
||||
F src/sqlite.h.in e429f66f9245c7f8675db24b230c950b8672ad1c
|
||||
F src/sqlite3ext.h 7d0d363ea7327e817ef0dfe1b7eee1f171b72890
|
||||
@@ -145,7 +131,7 @@ F test/analyze.test 2f55535aa335785db1a2f97d3f3831c16c09f8b0
|
||||
F test/async.test 464dc7c7ccb144e8c82ecca429e6d7cd1c96bd6e
|
||||
F test/async2.test 81e4a1fd010c903eb3b763fdb4c4cad7a99afb14
|
||||
F test/attach.test c616a88eab6b6fd99b7b2fcf449420f14628bc0b
|
||||
F test/attach2.test 0e6a7c54343c85dd877a1e86073a05176043ed40
|
||||
F test/attach2.test e685a5db6564a7ea8e864573163886f9e2dad5ab
|
||||
F test/attach3.test eafcafb107585aecc2ed1569a77914138eef46a9
|
||||
F test/attachmalloc.test 03eeddd06e685ddbe975efd51824e4941847e5f4
|
||||
F test/auth.test 66923137cf78475f5671b5e6e6274935e055aea0
|
||||
@@ -173,7 +159,7 @@ F test/busy.test 0271c854738e23ad76e10d4096a698e5af29d211
|
||||
F test/cache.test 9e530b55ba016ca17439f728a06898f0ade5f1da
|
||||
F test/capi2.test 7ecc9b342cc9ec27b53bbf95724cf2e5874fd496
|
||||
F test/capi3.test 1675323145d128e5942a9faffcfd5cf4e219a33f
|
||||
F test/capi3b.test 5f0bc94b104e11086b1103b20277e1910f59c7f4
|
||||
F test/capi3b.test 2c3d64873c95405ea43f05d076c9e3258aef7aab
|
||||
F test/capi3c.test 96e35164739c6fe3357fa36f0fe74bc23abc8ef7
|
||||
F test/cast.test f88e7b6946e9a467cf4bb142d92bb65a83747fc2
|
||||
F test/check.test e5ea0c1a06c10e81e3434ca029e2c4a562f2b673
|
||||
@@ -190,7 +176,6 @@ F test/corrupt2.test 572f8df0303d0ce63ddad5c5c9101a83a345ae46
|
||||
F test/corrupt3.test 263e8bb04e2728df832fddf6973cf54c91db0c32
|
||||
F test/crash.test 167eb4652eccbedb199b6f21850346c3f5d779fb
|
||||
F test/crash2.test 423c6ec404d15b7d7d0e40aef0a26740cce6075f
|
||||
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
|
||||
F test/date.test 09786cf0145147014867d822224b9bced2012b61
|
||||
F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb
|
||||
F test/delete.test 525a6953bc3978780cae35f3eaf1027cf4ce887d
|
||||
@@ -326,7 +311,7 @@ F test/table.test feea6a3eb08cf166f570255eea5447e42ef82498
|
||||
F test/tableapi.test 036575a98dcce7c92e9f39056839bbad8a715412
|
||||
F test/tclsqlite.test 51334389283c74bcbe28645a73159b17e239e9f3
|
||||
F test/temptable.test c36f3e5a94507abb64f7ba23deeb4e1a8a8c3821
|
||||
F test/tester.tcl effe3dae968afd8bb27c8792883788eeba821942
|
||||
F test/tester.tcl f5dcc50b4246ef035818c0cf94e84d65d82ac1ef
|
||||
F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
|
||||
F test/thread2.test 6d7b30102d600f51b4055ee3a5a19228799049fb
|
||||
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
|
||||
@@ -417,7 +402,6 @@ F www/audit.tcl 90e09d580f79c7efec0c7d6f447b7ec5c2dce5c0
|
||||
F www/autoinc.tcl b357f5ba954b046ee35392ce0f884a2fcfcdea06
|
||||
F www/c_interface.tcl b51b08591554c16a0c3ef718364a508ac25abc7e
|
||||
F www/capi3.tcl 7a7cc225fe02eb7ab861a6019b08baa0014409e1
|
||||
F www/capi3ref.tcl 80178d2697e97236c208a2a6a507e82d121acc71
|
||||
F www/changes.tcl 550300b0ff00fc1b872f7802b2d5a1e7587d3e58
|
||||
F www/common.tcl 2b793e5c31486c8a01dd27dc0a631ad93704438e
|
||||
F www/compile.tcl 276546d7eb445add5a867193bbd80f6919a6b084
|
||||
@@ -461,7 +445,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
|
||||
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
||||
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
|
||||
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
|
||||
P e278c4ef601eebeb5a4f89baf8b29a6794c403f1
|
||||
R ad363d72c9e972c93b0b5f5c7e1e7891
|
||||
P 2d2e68da74459340c262a6454fdd05149bc94c59
|
||||
R 781c90952ebd46fe48604936bd1e7056
|
||||
U drh
|
||||
Z 041345d6cc54720da9912c0e34152fab
|
||||
Z fc0778c193015e9c11bcde51c7863b39
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2d2e68da74459340c262a6454fdd05149bc94c59
|
||||
f0029da32f024c92fd6d304d7c38af5735ca321f
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
EXPORTS
|
||||
sqlite3_aggregate_context
|
||||
sqlite3_aggregate_count
|
||||
sqlite3_bind_blob
|
||||
sqlite3_bind_double
|
||||
sqlite3_bind_int
|
||||
sqlite3_bind_int64
|
||||
sqlite3_bind_null
|
||||
sqlite3_bind_parameter_count
|
||||
sqlite3_bind_parameter_index
|
||||
sqlite3_bind_parameter_name
|
||||
sqlite3_bind_text
|
||||
sqlite3_bind_text16
|
||||
sqlite3_busy_handler
|
||||
sqlite3_busy_timeout
|
||||
sqlite3_changes
|
||||
sqlite3_close
|
||||
sqlite3_collation_needed
|
||||
sqlite3_collation_needed16
|
||||
sqlite3_column_blob
|
||||
sqlite3_column_bytes
|
||||
sqlite3_column_bytes16
|
||||
sqlite3_column_count
|
||||
sqlite3_column_decltype
|
||||
sqlite3_column_decltype16
|
||||
sqlite3_column_double
|
||||
sqlite3_column_int
|
||||
sqlite3_column_int64
|
||||
sqlite3_column_name
|
||||
sqlite3_column_name16
|
||||
sqlite3_column_text
|
||||
sqlite3_column_text16
|
||||
sqlite3_column_type
|
||||
sqlite3_commit_hook
|
||||
sqlite3_complete
|
||||
sqlite3_complete16
|
||||
sqlite3_create_collation
|
||||
sqlite3_create_collation16
|
||||
sqlite3_create_function
|
||||
sqlite3_create_function16
|
||||
sqlite3_data_count
|
||||
sqlite3_db_handle
|
||||
sqlite3_enable_load_extension
|
||||
sqlite3_enable_shared_cache
|
||||
sqlite3_errcode
|
||||
sqlite3_errmsg
|
||||
sqlite3_errmsg16
|
||||
sqlite3_exec
|
||||
sqlite3_expired
|
||||
sqlite3_finalize
|
||||
sqlite3_free
|
||||
sqlite3_free_table
|
||||
sqlite3_get_autocommit
|
||||
sqlite3_get_auxdata
|
||||
sqlite3_get_table
|
||||
sqlite3_global_recover
|
||||
sqlite3_interrupt
|
||||
sqlite3_last_insert_rowid
|
||||
sqlite3_libversion
|
||||
sqlite3_libversion_number
|
||||
sqlite3_load_extension
|
||||
sqlite3_malloc
|
||||
sqlite3_mprintf
|
||||
sqlite3_open
|
||||
sqlite3_open16
|
||||
sqlite3_prepare
|
||||
sqlite3_prepare16
|
||||
sqlite3_progress_handler
|
||||
sqlite3_realloc
|
||||
sqlite3_reset
|
||||
sqlite3_result_blob
|
||||
sqlite3_result_double
|
||||
sqlite3_result_error
|
||||
sqlite3_result_error16
|
||||
sqlite3_result_int
|
||||
sqlite3_result_int64
|
||||
sqlite3_result_null
|
||||
sqlite3_result_text
|
||||
sqlite3_result_text16
|
||||
sqlite3_result_text16be
|
||||
sqlite3_result_text16le
|
||||
sqlite3_result_value
|
||||
sqlite3_rollback_hook
|
||||
sqlite3_set_authorizer
|
||||
sqlite3_set_auxdata
|
||||
sqlite3_snprintf
|
||||
sqlite3_step
|
||||
sqlite3_thread_cleanup
|
||||
sqlite3_total_changes
|
||||
sqlite3_trace
|
||||
sqlite3_transfer_bindings
|
||||
sqlite3_update_hook
|
||||
sqlite3_user_data
|
||||
sqlite3_value_blob
|
||||
sqlite3_value_bytes
|
||||
sqlite3_value_bytes16
|
||||
sqlite3_value_double
|
||||
sqlite3_value_int
|
||||
sqlite3_value_int64
|
||||
sqlite3_value_text
|
||||
sqlite3_value_text16
|
||||
sqlite3_value_text16be
|
||||
sqlite3_value_text16le
|
||||
sqlite3_value_type
|
||||
sqlite3_vmprintf
|
||||
+1
-2
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.358 2007/04/24 17:35:59 drh Exp $
|
||||
** $Id: btree.c,v 1.358.2.1 2007/08/14 13:20:27 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -5136,7 +5136,6 @@ static int balance_shallower(MemPage *pPage){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ) goto end_shallow_balance;
|
||||
releasePage(pChild);
|
||||
}
|
||||
end_shallow_balance:
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
** 2005 January 20
|
||||
**
|
||||
** 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 C code routines that are not a part of the official
|
||||
** SQLite API. These routines are unsupported.
|
||||
**
|
||||
** $Id: experimental.c,v 1.4 2006/01/31 20:49:13 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
** Set all the parameters in the compiled SQL statement to NULL.
|
||||
*/
|
||||
int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
for(i=1; rc==SQLITE_OK && i<=sqlite3_bind_parameter_count(pStmt); i++){
|
||||
rc = sqlite3_bind_null(pStmt, i);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sleep for a little while. Return the amount of time slept.
|
||||
*/
|
||||
int sqlite3_sleep(int ms){
|
||||
return sqlite3OsSleep(ms);
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
** SQLite uses this code for testing only. It is not a part of
|
||||
** the SQLite library. This file implements two new TCL commands
|
||||
** "md5" and "md5file" that compute md5 checksums on arbitrary text
|
||||
** and on complete files. These commands are used by the "testfixture"
|
||||
** program to help verify the correct operation of the SQLite library.
|
||||
**
|
||||
** The original use of these TCL commands was to test the ROLLBACK
|
||||
** feature of SQLite. First compute the MD5-checksum of the database.
|
||||
** Then make some changes but rollback the changes rather than commit
|
||||
** them. Compute a second MD5-checksum of the file and verify that the
|
||||
** two checksums are the same. Such is the original use of this code.
|
||||
** New uses may have been added since this comment was written.
|
||||
*/
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
#include <tcl.h>
|
||||
#include <string.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
* If compiled on a machine that doesn't have a 32-bit integer,
|
||||
* you just set "uint32" to the appropriate datatype for an
|
||||
* unsigned 32-bit integer. For example:
|
||||
*
|
||||
* cc -Duint32='unsigned long' md5.c
|
||||
*
|
||||
*/
|
||||
#ifndef uint32
|
||||
# define uint32 unsigned int
|
||||
#endif
|
||||
|
||||
struct Context {
|
||||
uint32 buf[4];
|
||||
uint32 bits[2];
|
||||
unsigned char in[64];
|
||||
};
|
||||
typedef char MD5Context[88];
|
||||
|
||||
/*
|
||||
* Note: this code is harmless on little-endian machines.
|
||||
*/
|
||||
static void byteReverse (unsigned char *buf, unsigned longs){
|
||||
uint32 t;
|
||||
do {
|
||||
t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
|
||||
((unsigned)buf[1]<<8 | buf[0]);
|
||||
*(uint32 *)buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
static void MD5Transform(uint32 buf[4], const uint32 in[16]){
|
||||
register uint32 a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
static void MD5Init(MD5Context *pCtx){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
static
|
||||
void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
uint32 t;
|
||||
|
||||
/* Update bitcount */
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
|
||||
ctx->bits[1]++; /* Carry from low to high */
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||
|
||||
/* Handle any leading odd-sized chunks */
|
||||
|
||||
if ( t ) {
|
||||
unsigned char *p = (unsigned char *)ctx->in + t;
|
||||
|
||||
t = 64-t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
/* Process data in 64-byte chunks */
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup - pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
|
||||
struct Context *ctx = (struct Context *)pCtx;
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
/* Set the first char of padding to 0x80. This is safe since there is
|
||||
always at least one byte free */
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
|
||||
/* Bytes of padding needed to make 64 bytes */
|
||||
count = 64 - 1 - count;
|
||||
|
||||
/* Pad out to 56 mod 64 */
|
||||
if (count < 8) {
|
||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
|
||||
/* Now fill the next block with 56 bytes */
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
/* Pad block to 56 bytes */
|
||||
memset(p, 0, count-8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
|
||||
((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32 *)ctx->in);
|
||||
byteReverse((unsigned char *)ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a digest into base-16. digest should be declared as
|
||||
** "unsigned char digest[16]" in the calling function. The MD5
|
||||
** digest is stored in the first 16 bytes. zBuf should
|
||||
** be "char zBuf[33]".
|
||||
*/
|
||||
static void DigestToBase16(unsigned char *digest, char *zBuf){
|
||||
static char const zEncode[] = "0123456789abcdef";
|
||||
int i, j;
|
||||
|
||||
for(j=i=0; i<16; i++){
|
||||
int a = digest[i];
|
||||
zBuf[j++] = zEncode[(a>>4)&0xf];
|
||||
zBuf[j++] = zEncode[a & 0xf];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command for md5. The argument is the text to be hashed. The
|
||||
** Result is the hash in base64.
|
||||
*/
|
||||
static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" TEXT\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, (unsigned char*)argv[1], (unsigned)strlen(argv[1]));
|
||||
MD5Final(digest, &ctx);
|
||||
DigestToBase16(digest, interp->result);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A TCL command to take the md5 hash of a file. The argument is the
|
||||
** name of the file.
|
||||
*/
|
||||
static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
|
||||
FILE *in;
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
char zBuf[10240];
|
||||
|
||||
if( argc!=2 ){
|
||||
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
|
||||
" FILENAME\"", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
in = fopen(argv[1],"rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
|
||||
"\" for reading", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
MD5Init(&ctx);
|
||||
for(;;){
|
||||
int n;
|
||||
n = fread(zBuf, 1, sizeof(zBuf), in);
|
||||
if( n<=0 ) break;
|
||||
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
|
||||
}
|
||||
fclose(in);
|
||||
MD5Final(digest, &ctx);
|
||||
DigestToBase16(digest, interp->result);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the two TCL commands above with the TCL interpreter.
|
||||
*/
|
||||
int Md5_Init(Tcl_Interp *interp){
|
||||
Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
|
||||
Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** During testing, the special md5sum() aggregate function is available.
|
||||
** inside SQLite. The following routines implement that function.
|
||||
*/
|
||||
static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
MD5Context *p;
|
||||
int i;
|
||||
if( argc<1 ) return;
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
if( sqlite3_aggregate_count(context)==1 ){
|
||||
MD5Init(p);
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
const char *zData = (char*)sqlite3_value_text(argv[i]);
|
||||
if( zData ){
|
||||
MD5Update(p, (unsigned char*)zData, strlen(zData));
|
||||
}
|
||||
}
|
||||
}
|
||||
static void md5finalize(sqlite3_context *context){
|
||||
MD5Context *p;
|
||||
unsigned char digest[16];
|
||||
char zBuf[33];
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
MD5Final(digest,p);
|
||||
DigestToBase16(digest, zBuf);
|
||||
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
void Md5_Register(sqlite3 *db){
|
||||
sqlite3_create_function(db, "md5sum", -1, SQLITE_UTF8, 0, 0,
|
||||
md5step, md5finalize);
|
||||
}
|
||||
-463
@@ -1,463 +0,0 @@
|
||||
/*
|
||||
** 2004 May 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 is specific to Unix systems. It is used
|
||||
** for testing SQLite only.
|
||||
*/
|
||||
#if OS_TEST /* This file is used for the test backend only */
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h" /* Must be first to enable large file support */
|
||||
|
||||
#define sqlite3OsOpenReadWrite sqlite3RealOpenReadWrite
|
||||
#define sqlite3OsOpenExclusive sqlite3RealOpenExclusive
|
||||
#define sqlite3OsOpenReadOnly sqlite3RealOpenReadOnly
|
||||
#define sqlite3OsOpenDirectory sqlite3RealOpenDirectory
|
||||
#define sqlite3OsClose sqlite3RealClose
|
||||
#define sqlite3OsRead sqlite3RealRead
|
||||
#define sqlite3OsWrite sqlite3RealWrite
|
||||
#define sqlite3OsSeek sqlite3RealSeek
|
||||
#define sqlite3OsSync sqlite3RealSync
|
||||
#define sqlite3OsTruncate sqlite3RealTruncate
|
||||
#define sqlite3OsFileSize sqlite3RealFileSize
|
||||
#define sqlite3OsLock sqlite3RealLock
|
||||
#define sqlite3OsUnlock sqlite3RealUnlock
|
||||
#define sqlite3OsCheckReservedLock sqlite3RealCheckReservedLock
|
||||
|
||||
#define OsFile OsRealFile
|
||||
#define OS_UNIX 1
|
||||
#include "os_unix.c"
|
||||
#undef OS_UNIX
|
||||
#undef OsFile
|
||||
|
||||
#undef sqlite3OsOpenReadWrite
|
||||
#undef sqlite3OsOpenExclusive
|
||||
#undef sqlite3OsOpenReadOnly
|
||||
#undef sqlite3OsOpenDirectory
|
||||
#undef sqlite3OsClose
|
||||
#undef sqlite3OsRead
|
||||
#undef sqlite3OsWrite
|
||||
#undef sqlite3OsSeek
|
||||
#undef sqlite3OsSync
|
||||
#undef sqlite3OsTruncate
|
||||
#undef sqlite3OsFileSize
|
||||
#undef sqlite3OsLock
|
||||
#undef sqlite3OsUnlock
|
||||
#undef sqlite3OsCheckReservedLock
|
||||
|
||||
#define BLOCKSIZE 512
|
||||
#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE)
|
||||
|
||||
|
||||
/*
|
||||
** The following variables control when a simulated crash occurs.
|
||||
**
|
||||
** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
|
||||
** a file that SQLite will call sqlite3OsSync() on. Each time this happens
|
||||
** iCrashDelay is decremented. If iCrashDelay is zero after being
|
||||
** decremented, a "crash" occurs during the sync() operation.
|
||||
**
|
||||
** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
|
||||
** synced.
|
||||
*/
|
||||
static int iCrashDelay = 0;
|
||||
char zCrashFile[256];
|
||||
|
||||
/*
|
||||
** Set the value of the two crash parameters.
|
||||
*/
|
||||
void sqlite3SetCrashParams(int iDelay, char const *zFile){
|
||||
sqlite3OsEnterMutex();
|
||||
assert( strlen(zFile)<256 );
|
||||
strcpy(zCrashFile, zFile);
|
||||
iCrashDelay = iDelay;
|
||||
sqlite3OsLeaveMutex();
|
||||
}
|
||||
|
||||
/*
|
||||
** File zPath is being sync()ed. Return non-zero if this should
|
||||
** cause a crash.
|
||||
*/
|
||||
static int crashRequired(char const *zPath){
|
||||
int r;
|
||||
int n;
|
||||
sqlite3OsEnterMutex();
|
||||
n = strlen(zCrashFile);
|
||||
if( zCrashFile[n-1]=='*' ){
|
||||
n--;
|
||||
}else if( strlen(zPath)>n ){
|
||||
n = strlen(zPath);
|
||||
}
|
||||
r = 0;
|
||||
if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
|
||||
iCrashDelay--;
|
||||
if( iCrashDelay<=0 ){
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static OsTestFile *pAllFiles = 0;
|
||||
|
||||
/*
|
||||
** Initialise the os_test.c specific fields of pFile.
|
||||
*/
|
||||
static void initFile(OsFile *id, char const *zName){
|
||||
OsTestFile *pFile = (OsTestFile *)
|
||||
sqliteMalloc(sizeof(OsTestFile) + strlen(zName)+1);
|
||||
pFile->nMaxWrite = 0;
|
||||
pFile->nBlk = 0;
|
||||
pFile->apBlk = 0;
|
||||
pFile->zName = (char *)(&pFile[1]);
|
||||
strcpy(pFile->zName, zName);
|
||||
*id = pFile;
|
||||
pFile->pNext = pAllFiles;
|
||||
pAllFiles = pFile;
|
||||
}
|
||||
|
||||
/*
|
||||
** Undo the work done by initFile. Delete the OsTestFile structure
|
||||
** and unlink the structure from the pAllFiles list.
|
||||
*/
|
||||
static void closeFile(OsFile *id){
|
||||
OsTestFile *pFile = *id;
|
||||
if( pFile==pAllFiles ){
|
||||
pAllFiles = pFile->pNext;
|
||||
}else{
|
||||
OsTestFile *p;
|
||||
for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){
|
||||
assert( p );
|
||||
}
|
||||
p->pNext = pFile->pNext;
|
||||
}
|
||||
sqliteFree(pFile);
|
||||
*id = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current seek offset from the start of the file. This
|
||||
** is unix-only code.
|
||||
*/
|
||||
static i64 osTell(OsTestFile *pFile){
|
||||
return lseek(pFile->fd.h, 0, SEEK_CUR);
|
||||
}
|
||||
|
||||
/*
|
||||
** Load block 'blk' into the cache of pFile.
|
||||
*/
|
||||
static int cacheBlock(OsTestFile *pFile, int blk){
|
||||
if( blk>=pFile->nBlk ){
|
||||
int n = ((pFile->nBlk * 2) + 100 + blk);
|
||||
/* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
|
||||
pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
|
||||
if( !pFile->apBlk ) return SQLITE_NOMEM;
|
||||
memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
|
||||
pFile->nBlk = n;
|
||||
}
|
||||
|
||||
if( !pFile->apBlk[blk] ){
|
||||
i64 filesize;
|
||||
int rc;
|
||||
|
||||
u8 *p = sqliteMalloc(BLOCKSIZE);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
pFile->apBlk[blk] = p;
|
||||
|
||||
rc = sqlite3RealFileSize(&pFile->fd, &filesize);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
if( BLOCK_OFFSET(blk)<filesize ){
|
||||
int len = BLOCKSIZE;
|
||||
rc = sqlite3RealSeek(&pFile->fd, blk*BLOCKSIZE);
|
||||
if( BLOCK_OFFSET(blk+1)>filesize ){
|
||||
len = filesize - BLOCK_OFFSET(blk);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3RealRead(&pFile->fd, p, len);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* #define TRACE_WRITECACHE */
|
||||
|
||||
/*
|
||||
** Write the cache of pFile to disk. If crash is non-zero, randomly
|
||||
** skip blocks when writing. The cache is deleted before returning.
|
||||
*/
|
||||
static int writeCache2(OsTestFile *pFile, int crash){
|
||||
int i;
|
||||
int nMax = pFile->nMaxWrite;
|
||||
i64 offset;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
offset = osTell(pFile);
|
||||
for(i=0; i<pFile->nBlk; i++){
|
||||
u8 *p = pFile->apBlk[i];
|
||||
if( p ){
|
||||
int skip = 0;
|
||||
int trash = 0;
|
||||
if( crash ){
|
||||
char random;
|
||||
sqlite3Randomness(1, &random);
|
||||
if( random & 0x01 ){
|
||||
if( random & 0x02 ){
|
||||
trash = 1;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Trashing block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}else{
|
||||
skip = 1;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Skiping block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}
|
||||
}else{
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("Writing block %d of %s\n", i, pFile->zName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i));
|
||||
}
|
||||
if( rc==SQLITE_OK && !skip ){
|
||||
int len = BLOCKSIZE;
|
||||
if( BLOCK_OFFSET(i+1)>nMax ){
|
||||
len = nMax-BLOCK_OFFSET(i);
|
||||
}
|
||||
if( len>0 ){
|
||||
if( trash ){
|
||||
sqlite3Randomness(len, p);
|
||||
}
|
||||
rc = sqlite3RealWrite(&pFile->fd, p, len);
|
||||
}
|
||||
}
|
||||
sqliteFree(p);
|
||||
}
|
||||
}
|
||||
sqliteFree(pFile->apBlk);
|
||||
pFile->nBlk = 0;
|
||||
pFile->apBlk = 0;
|
||||
pFile->nMaxWrite = 0;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3RealSeek(&pFile->fd, offset);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write the cache to disk.
|
||||
*/
|
||||
static int writeCache(OsTestFile *pFile){
|
||||
if( pFile->apBlk ){
|
||||
int c = crashRequired(pFile->zName);
|
||||
if( c ){
|
||||
OsTestFile *p;
|
||||
#ifdef TRACE_WRITECACHE
|
||||
printf("\nCrash during sync of %s\n", pFile->zName);
|
||||
#endif
|
||||
for(p=pAllFiles; p; p=p->pNext){
|
||||
writeCache2(p, 1);
|
||||
}
|
||||
exit(-1);
|
||||
}else{
|
||||
return writeCache2(pFile, 0);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the file.
|
||||
*/
|
||||
int sqlite3OsClose(OsFile *id){
|
||||
if( !(*id) ) return SQLITE_OK;
|
||||
if( (*id)->fd.isOpen ){
|
||||
/* printf("CLOSE %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */
|
||||
writeCache(*id);
|
||||
sqlite3RealClose(&(*id)->fd);
|
||||
}
|
||||
closeFile(id);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
|
||||
i64 offset; /* The current offset from the start of the file */
|
||||
i64 end; /* The byte just past the last byte read */
|
||||
int blk; /* Block number the read starts on */
|
||||
int i;
|
||||
u8 *zCsr;
|
||||
int rc = SQLITE_OK;
|
||||
OsTestFile *pFile = *id;
|
||||
|
||||
offset = osTell(pFile);
|
||||
end = offset+amt;
|
||||
blk = (offset/BLOCKSIZE);
|
||||
|
||||
zCsr = (u8 *)pBuf;
|
||||
for(i=blk; i*BLOCKSIZE<end; i++){
|
||||
int off = 0;
|
||||
int len = 0;
|
||||
|
||||
|
||||
if( BLOCK_OFFSET(i) < offset ){
|
||||
off = offset-BLOCK_OFFSET(i);
|
||||
}
|
||||
len = BLOCKSIZE - off;
|
||||
if( BLOCK_OFFSET(i+1) > end ){
|
||||
len = len - (BLOCK_OFFSET(i+1)-end);
|
||||
}
|
||||
|
||||
if( i<pFile->nBlk && pFile->apBlk[i]){
|
||||
u8 *pBlk = pFile->apBlk[i];
|
||||
memcpy(zCsr, &pBlk[off], len);
|
||||
}else{
|
||||
rc = sqlite3RealSeek(&pFile->fd, BLOCK_OFFSET(i) + off);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3RealRead(&pFile->fd, zCsr, len);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
|
||||
zCsr += len;
|
||||
}
|
||||
assert( zCsr==&((u8 *)pBuf)[amt] );
|
||||
|
||||
rc = sqlite3RealSeek(&pFile->fd, end);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
|
||||
i64 offset; /* The current offset from the start of the file */
|
||||
i64 end; /* The byte just past the last byte written */
|
||||
int blk; /* Block number the write starts on */
|
||||
int i;
|
||||
const u8 *zCsr;
|
||||
int rc = SQLITE_OK;
|
||||
OsTestFile *pFile = *id;
|
||||
|
||||
offset = osTell(pFile);
|
||||
end = offset+amt;
|
||||
blk = (offset/BLOCKSIZE);
|
||||
|
||||
zCsr = (u8 *)pBuf;
|
||||
for(i=blk; i*BLOCKSIZE<end; i++){
|
||||
u8 *pBlk;
|
||||
int off = 0;
|
||||
int len = 0;
|
||||
|
||||
/* Make sure the block is in the cache */
|
||||
rc = cacheBlock(pFile, i);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Write into the cache */
|
||||
pBlk = pFile->apBlk[i];
|
||||
assert( pBlk );
|
||||
|
||||
if( BLOCK_OFFSET(i) < offset ){
|
||||
off = offset-BLOCK_OFFSET(i);
|
||||
}
|
||||
len = BLOCKSIZE - off;
|
||||
if( BLOCK_OFFSET(i+1) > end ){
|
||||
len = len - (BLOCK_OFFSET(i+1)-end);
|
||||
}
|
||||
memcpy(&pBlk[off], zCsr, len);
|
||||
zCsr += len;
|
||||
}
|
||||
if( pFile->nMaxWrite<end ){
|
||||
pFile->nMaxWrite = end;
|
||||
}
|
||||
assert( zCsr==&((u8 *)pBuf)[amt] );
|
||||
|
||||
rc = sqlite3RealSeek(&pFile->fd, end);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the file. First flush the write-cache to disk, then call the
|
||||
** real sync() function.
|
||||
*/
|
||||
int sqlite3OsSync(OsFile *id, int dataOnly){
|
||||
int rc;
|
||||
/* printf("SYNC %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */
|
||||
rc = writeCache(*id);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = sqlite3RealSync(&(*id)->fd, dataOnly);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new
|
||||
** file size to ensure that nothing in the write-cache past this point
|
||||
** is written to disk.
|
||||
*/
|
||||
int sqlite3OsTruncate(OsFile *id, i64 nByte){
|
||||
(*id)->nMaxWrite = nByte;
|
||||
return sqlite3RealTruncate(&(*id)->fd, nByte);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the file. If the cache contains a write that extended
|
||||
** the file, then return this size instead of the on-disk size.
|
||||
*/
|
||||
int sqlite3OsFileSize(OsFile *id, i64 *pSize){
|
||||
int rc = sqlite3RealFileSize(&(*id)->fd, pSize);
|
||||
if( rc==SQLITE_OK && pSize && *pSize<(*id)->nMaxWrite ){
|
||||
*pSize = (*id)->nMaxWrite;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** The three functions used to open files. All that is required is to
|
||||
** initialise the os_test.c specific fields and then call the corresponding
|
||||
** os_unix.c function to really open the file.
|
||||
*/
|
||||
int sqlite3OsOpenReadWrite(const char *zFilename, OsFile *id, int *pReadonly){
|
||||
initFile(id, zFilename);
|
||||
return sqlite3RealOpenReadWrite(zFilename, &(*id)->fd, pReadonly);
|
||||
}
|
||||
int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
initFile(id, zFilename);
|
||||
return sqlite3RealOpenExclusive(zFilename, &(*id)->fd, delFlag);
|
||||
}
|
||||
int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
|
||||
initFile(id, zFilename);
|
||||
return sqlite3RealOpenReadOnly(zFilename, &(*id)->fd);
|
||||
}
|
||||
|
||||
/*
|
||||
** These six function calls are passed straight through to the os_unix.c
|
||||
** backend.
|
||||
*/
|
||||
int sqlite3OsSeek(OsFile *id, i64 offset){
|
||||
return sqlite3RealSeek(&(*id)->fd, offset);
|
||||
}
|
||||
int sqlite3OsCheckReservedLock(OsFile *id){
|
||||
return sqlite3RealCheckReservedLock(&(*id)->fd);
|
||||
}
|
||||
int sqlite3OsLock(OsFile *id, int locktype){
|
||||
return sqlite3RealLock(&(*id)->fd, locktype);
|
||||
}
|
||||
int sqlite3OsUnlock(OsFile *id, int locktype){
|
||||
return sqlite3RealUnlock(&(*id)->fd, locktype);
|
||||
}
|
||||
int sqlite3OsOpenDirectory(const char *zDirname, OsFile *id){
|
||||
return sqlite3RealOpenDirectory(zDirname, &(*id)->fd);
|
||||
}
|
||||
|
||||
#endif /* OS_TEST */
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
** 2004 May 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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
*/
|
||||
#ifndef _SQLITE_OS_TEST_H_
|
||||
#define _SQLITE_OS_TEST_H_
|
||||
|
||||
#define OsFile OsRealFile
|
||||
#define OS_UNIX 1
|
||||
#include "os_unix.h"
|
||||
#undef OS_UNIX
|
||||
#undef OsFile
|
||||
#undef SET_FULLSYNC
|
||||
|
||||
/* Include sqliteInt.h now to get the type u8. */
|
||||
#include "sqliteInt.h"
|
||||
|
||||
typedef struct OsTestFile* OsFile;
|
||||
typedef struct OsTestFile OsTestFile;
|
||||
struct OsTestFile {
|
||||
u8 **apBlk; /* Array of blocks that have been written to. */
|
||||
int nBlk; /* Size of apBlock. */
|
||||
int nMaxWrite; /* Largest offset written to. */
|
||||
char *zName; /* File name */
|
||||
OsRealFile fd;
|
||||
OsTestFile *pNext;
|
||||
};
|
||||
|
||||
void sqlite3SetCrashParams(int iDelay, char const *zFile);
|
||||
|
||||
#endif /* _SQLITE_OS_UNIX_H_ */
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
** 2004 May 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 header file defined OS-specific features for Unix.
|
||||
*/
|
||||
#ifndef _SQLITE_OS_UNIX_H_
|
||||
#define _SQLITE_OS_UNIX_H_
|
||||
|
||||
/*
|
||||
** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE
|
||||
** to the compiler command line.
|
||||
*/
|
||||
|
||||
/*
|
||||
** These #defines should enable >2GB file support on Posix if the
|
||||
** underlying operating system supports it. If the OS lacks
|
||||
** large file support, or if the OS is windows, these should be no-ops.
|
||||
**
|
||||
** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
|
||||
** on the compiler command line. This is necessary if you are compiling
|
||||
** on a recent machine (ex: RedHat 7.2) but you want your code to work
|
||||
** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
|
||||
** without this option, LFS is enable. But LFS does not exist in the kernel
|
||||
** in RedHat 6.0, so the code won't work. Hence, for maximum binary
|
||||
** portability you should omit LFS.
|
||||
**
|
||||
** Similar is true for MacOS. LFS is only supported on MacOS 9 and later.
|
||||
*/
|
||||
#ifndef SQLITE_DISABLE_LFS
|
||||
# define _LARGE_FILE 1
|
||||
# ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# endif
|
||||
# define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** standard include files.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
** Macros used to determine whether or not to use threads. The
|
||||
** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
|
||||
** Posix threads and SQLITE_W32_THREADS is defined if we are
|
||||
** synchronizing using Win32 threads.
|
||||
*/
|
||||
#if defined(THREADSAFE) && THREADSAFE
|
||||
# include <pthread.h>
|
||||
# define SQLITE_UNIX_THREADS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The OsFile structure is a operating-system independing representation
|
||||
** of an open file handle. It is defined differently for each architecture.
|
||||
**
|
||||
** This is the definition for Unix.
|
||||
**
|
||||
** OsFile.locktype takes one of the values SHARED_LOCK, RESERVED_LOCK,
|
||||
** PENDING_LOCK or EXCLUSIVE_LOCK.
|
||||
*/
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
struct Pager *pPager; /* The pager that owns this OsFile. Might be 0 */
|
||||
struct openCnt *pOpen; /* Info about all open fd's on this inode */
|
||||
struct lockInfo *pLock; /* Info about locks on this inode */
|
||||
int h; /* The file descriptor */
|
||||
unsigned char locktype; /* The type of lock held on this fd */
|
||||
unsigned char isOpen; /* True if needs to be closed */
|
||||
unsigned char fullSync; /* Use F_FULLSYNC if available */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
#ifdef SQLITE_UNIX_THREADS
|
||||
pthread_t tid; /* The thread authorized to use this OsFile */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** A macro to set the OsFile.fullSync flag, if it exists.
|
||||
*/
|
||||
#define SET_FULLSYNC(x,y) ((x).fullSync = (y))
|
||||
|
||||
/*
|
||||
** Maximum number of characters in a temporary file name
|
||||
*/
|
||||
#define SQLITE_TEMPNAME_SIZE 200
|
||||
|
||||
/*
|
||||
** Minimum interval supported by sqlite3OsSleep().
|
||||
*/
|
||||
#if defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
# define SQLITE_MIN_SLEEP_MS 1
|
||||
#else
|
||||
# define SQLITE_MIN_SLEEP_MS 1000
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Default permissions when creating a new file
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS
|
||||
# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _SQLITE_OS_UNIX_H_ */
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
** 2004 May 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 header file defines OS-specific features for Win32
|
||||
*/
|
||||
#ifndef _SQLITE_OS_WIN_H_
|
||||
#define _SQLITE_OS_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
|
||||
/*
|
||||
** The OsFile structure is a operating-system independing representation
|
||||
** of an open file handle. It is defined differently for each architecture.
|
||||
**
|
||||
** This is the definition for Win32.
|
||||
*/
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
HANDLE h; /* Handle for accessing the file */
|
||||
unsigned char locktype; /* Type of lock currently held on this file */
|
||||
unsigned char isOpen; /* True if needs to be closed */
|
||||
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
|
||||
};
|
||||
|
||||
|
||||
#define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
|
||||
#define SQLITE_MIN_SLEEP_MS 1
|
||||
|
||||
|
||||
#endif /* _SQLITE_OS_WIN_H_ */
|
||||
+52
-29
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.329 2007/04/16 15:02:19 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.329.2.1 2007/08/14 13:20:28 drh Exp $
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DISKIO
|
||||
#include "sqliteInt.h"
|
||||
@@ -506,7 +506,11 @@ static u32 retrieve32bits(PgHdr *p, int offset){
|
||||
*/
|
||||
static int pager_error(Pager *pPager, int rc){
|
||||
int rc2 = rc & 0xff;
|
||||
assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK );
|
||||
assert(
|
||||
pPager->errCode==SQLITE_FULL ||
|
||||
pPager->errCode==SQLITE_OK ||
|
||||
(pPager->errCode & 0xff)==SQLITE_IOERR
|
||||
);
|
||||
if(
|
||||
rc2==SQLITE_FULL ||
|
||||
rc2==SQLITE_IOERR ||
|
||||
@@ -1086,6 +1090,10 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
|
||||
** page in the pager cache. In this case just update the pager cache,
|
||||
** not the database file. The page is left marked dirty in this case.
|
||||
**
|
||||
** If a malloc() or I/O error occurs during a Movepage() call then the
|
||||
** page might not be in cache. So the condition described in the
|
||||
** above paragraph is not assertable.
|
||||
**
|
||||
** If in EXCLUSIVE state, then we update the pager cache if it exists
|
||||
** and the main file. The page is then marked not dirty.
|
||||
**
|
||||
@@ -1094,16 +1102,16 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
|
||||
** This occurs when a page is changed prior to the start of a statement
|
||||
** then changed again within the statement. When rolling back such a
|
||||
** statement we must not write to the original database unless we know
|
||||
** for certain that original page contents are in the main rollback
|
||||
** journal. Otherwise, if a full ROLLBACK occurs after the statement
|
||||
** rollback the full ROLLBACK will not restore the page to its original
|
||||
** content. Two conditions must be met before writing to the database
|
||||
** files. (1) the database must be locked. (2) we know that the original
|
||||
** page content is in the main journal either because the page is not in
|
||||
** cache or else it is marked as needSync==0.
|
||||
** for certain that original page contents are synced into the main rollback
|
||||
** journal. Otherwise, a power loss might leave modified data in the
|
||||
** database file without an entry in the rollback journal that can
|
||||
** restore the database to its original form. Two conditions must be
|
||||
** met before writing to the database files. (1) the database must be
|
||||
** locked. (2) we know that the original page content is fully synced
|
||||
** in the main journal either because the page is not in cache or else
|
||||
** the page is marked as needSync==0.
|
||||
*/
|
||||
pPg = pager_lookup(pPager, pgno);
|
||||
assert( pPager->state>=PAGER_EXCLUSIVE || pPg!=0 );
|
||||
PAGERTRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno);
|
||||
if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){
|
||||
rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
|
||||
@@ -1364,10 +1372,15 @@ static int pager_playback(Pager *pPager, int isHot){
|
||||
}
|
||||
|
||||
/* If nRec is 0 and this rollback is of a transaction created by this
|
||||
** process. In this case the rest of the journal file consists of
|
||||
** journalled copies of pages that need to be read back into the cache.
|
||||
** process and if this is the final header in the journal, then it means
|
||||
** that this part of the journal was being filled but has not yet been
|
||||
** synced to disk. Compute the number of pages based on the remaining
|
||||
** size of the file.
|
||||
**
|
||||
** The third term of the test was added to fix ticket #2565.
|
||||
*/
|
||||
if( nRec==0 && !isHot ){
|
||||
if( nRec==0 && !isHot &&
|
||||
pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){
|
||||
nRec = (szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager);
|
||||
}
|
||||
|
||||
@@ -2647,7 +2660,7 @@ int sqlite3PagerReleaseMemory(int nReq){
|
||||
pTmp->pNextAll = pPg->pNextAll;
|
||||
}
|
||||
nReleased += sqliteAllocSize(pPg);
|
||||
IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
|
||||
IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
|
||||
PAGER_INCR(sqlite3_pager_pgfree_count);
|
||||
sqliteFree(pPg);
|
||||
}
|
||||
@@ -2659,7 +2672,11 @@ int sqlite3PagerReleaseMemory(int nReq){
|
||||
** The error will be returned to the user (or users, in the case
|
||||
** of a shared pager cache) of the pager for which the error occured.
|
||||
*/
|
||||
assert( (rc&0xff)==SQLITE_IOERR || rc==SQLITE_FULL );
|
||||
assert(
|
||||
(rc&0xff)==SQLITE_IOERR ||
|
||||
rc==SQLITE_FULL ||
|
||||
rc==SQLITE_BUSY
|
||||
);
|
||||
assert( pPager->state>=PAGER_RESERVED );
|
||||
pager_error(pPager, rc);
|
||||
}
|
||||
@@ -4161,15 +4178,15 @@ void sqlite3PagerSetCodec(
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
/*
|
||||
** Move the page identified by pData to location pgno in the file.
|
||||
** Move the page pPg to location pgno in the file.
|
||||
**
|
||||
** There must be no references to the current page pgno. If current page
|
||||
** pgno is not already in the rollback journal, it is not written there by
|
||||
** by this routine. The same applies to the page pData refers to on entry to
|
||||
** this routine.
|
||||
** There must be no references to the page previously located at
|
||||
** pgno (which we call pPgOld) though that page is allowed to be
|
||||
** in cache. If the page previous located at pgno is not already
|
||||
** in the rollback journal, it is not put there by by this routine.
|
||||
**
|
||||
** References to the page refered to by pData remain valid. Updating any
|
||||
** meta-data associated with page pData (i.e. data stored in the nExtra bytes
|
||||
** References to the page pPg remain valid. Updating any
|
||||
** meta-data associated with pPg (i.e. data stored in the nExtra bytes
|
||||
** allocated along with the page) is the responsibility of the caller.
|
||||
**
|
||||
** A transaction must be active when this routine is called. It used to be
|
||||
@@ -4178,7 +4195,7 @@ void sqlite3PagerSetCodec(
|
||||
** transaction is active).
|
||||
*/
|
||||
int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
|
||||
PgHdr *pPgOld;
|
||||
PgHdr *pPgOld; /* The page being overwritten. */
|
||||
int h;
|
||||
Pgno needSyncPgno = 0;
|
||||
|
||||
@@ -4203,17 +4220,23 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
|
||||
** page pgno before the 'move' operation, it needs to be retained
|
||||
** for the page moved there.
|
||||
*/
|
||||
pPg->needSync = 0;
|
||||
pPgOld = pager_lookup(pPager, pgno);
|
||||
if( pPgOld ){
|
||||
assert( pPgOld->nRef==0 );
|
||||
unlinkHashChain(pPager, pPgOld);
|
||||
makeClean(pPgOld);
|
||||
if( pPgOld->needSync ){
|
||||
assert( pPgOld->inJournal );
|
||||
pPg->inJournal = 1;
|
||||
pPg->needSync = 1;
|
||||
assert( pPager->needSync );
|
||||
}
|
||||
pPg->needSync = pPgOld->needSync;
|
||||
}else{
|
||||
pPg->needSync = 0;
|
||||
}
|
||||
if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
|
||||
pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
|
||||
}else if( (int)pgno>=pPager->origDbSize ){
|
||||
pPg->inJournal = 1;
|
||||
}else{
|
||||
pPg->inJournal = 0;
|
||||
assert( pPg->needSync==0 );
|
||||
}
|
||||
|
||||
/* Change the page number for pPg and insert it into the new hash-chain. */
|
||||
|
||||
-485
@@ -1,485 +0,0 @@
|
||||
/*
|
||||
** 2006 January 07
|
||||
**
|
||||
** 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 demonstration code. Nothing in this file gets compiled
|
||||
** or linked into the SQLite library unless you use a non-standard option:
|
||||
**
|
||||
** -DSQLITE_SERVER=1
|
||||
**
|
||||
** The configure script will never generate a Makefile with the option
|
||||
** above. You will need to manually modify the Makefile if you want to
|
||||
** include any of the code from this file in your project. Or, at your
|
||||
** option, you may copy and paste the code from this file and
|
||||
** thereby avoiding a recompile of SQLite.
|
||||
**
|
||||
**
|
||||
** This source file demonstrates how to use SQLite to create an SQL database
|
||||
** server thread in a multiple-threaded program. One or more client threads
|
||||
** send messages to the server thread and the server thread processes those
|
||||
** messages in the order received and returns the results to the client.
|
||||
**
|
||||
** One might ask: "Why bother? Why not just let each thread connect
|
||||
** to the database directly?" There are a several of reasons to
|
||||
** prefer the client/server approach.
|
||||
**
|
||||
** (1) Some systems (ex: Redhat9) have broken threading implementations
|
||||
** that prevent SQLite database connections from being used in
|
||||
** a thread different from the one where they were created. With
|
||||
** the client/server approach, all database connections are created
|
||||
** and used within the server thread. Client calls to the database
|
||||
** can be made from multiple threads (though not at the same time!)
|
||||
**
|
||||
** (2) Beginning with SQLite version 3.3.0, when two or more
|
||||
** connections to the same database occur within the same thread,
|
||||
** they can optionally share their database cache. This reduces
|
||||
** I/O and memory requirements. Cache shared is controlled using
|
||||
** the sqlite3_enable_shared_cache() API.
|
||||
**
|
||||
** (3) Database connections on a shared cache use table-level locking
|
||||
** instead of file-level locking for improved concurrency.
|
||||
**
|
||||
** (4) Database connections on a shared cache can by optionally
|
||||
** set to READ UNCOMMITTED isolation. (The default isolation for
|
||||
** SQLite is SERIALIZABLE.) When this occurs, readers will
|
||||
** never be blocked by a writer and writers will not be
|
||||
** blocked by readers. There can still only be a single writer
|
||||
** at a time, but multiple readers can simultaneously exist with
|
||||
** that writer. This is a huge increase in concurrency.
|
||||
**
|
||||
** To summarize the rational for using a client/server approach: prior
|
||||
** to SQLite version 3.3.0 it probably was not worth the trouble. But
|
||||
** with SQLite version 3.3.0 and beyond you can get significant performance
|
||||
** and concurrency improvements and memory usage reductions by going
|
||||
** client/server.
|
||||
**
|
||||
** Note: The extra features of version 3.3.0 described by points (2)
|
||||
** through (4) above are only available if you compile without the
|
||||
** option -DSQLITE_OMIT_SHARED_CACHE.
|
||||
**
|
||||
** Here is how the client/server approach works: The database server
|
||||
** thread is started on this procedure:
|
||||
**
|
||||
** void *sqlite3_server(void *NotUsed);
|
||||
**
|
||||
** The sqlite_server procedure runs as long as the g.serverHalt variable
|
||||
** is false. A mutex is used to make sure no more than one server runs
|
||||
** at a time. The server waits for messages to arrive on a message
|
||||
** queue and processes the messages in order.
|
||||
**
|
||||
** Two convenience routines are provided for starting and stopping the
|
||||
** server thread:
|
||||
**
|
||||
** void sqlite3_server_start(void);
|
||||
** void sqlite3_server_stop(void);
|
||||
**
|
||||
** Both of the convenience routines return immediately. Neither will
|
||||
** ever give an error. If a server is already started or already halted,
|
||||
** then the routines are effectively no-ops.
|
||||
**
|
||||
** Clients use the following interfaces:
|
||||
**
|
||||
** sqlite3_client_open
|
||||
** sqlite3_client_prepare
|
||||
** sqlite3_client_step
|
||||
** sqlite3_client_reset
|
||||
** sqlite3_client_finalize
|
||||
** sqlite3_client_close
|
||||
**
|
||||
** These interfaces work exactly like the standard core SQLite interfaces
|
||||
** having the same names without the "_client_" infix. Many other SQLite
|
||||
** interfaces can be used directly without having to send messages to the
|
||||
** server as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined.
|
||||
** The following interfaces fall into this second category:
|
||||
**
|
||||
** sqlite3_bind_*
|
||||
** sqlite3_changes
|
||||
** sqlite3_clear_bindings
|
||||
** sqlite3_column_*
|
||||
** sqlite3_complete
|
||||
** sqlite3_create_collation
|
||||
** sqlite3_create_function
|
||||
** sqlite3_data_count
|
||||
** sqlite3_db_handle
|
||||
** sqlite3_errcode
|
||||
** sqlite3_errmsg
|
||||
** sqlite3_last_insert_rowid
|
||||
** sqlite3_total_changes
|
||||
** sqlite3_transfer_bindings
|
||||
**
|
||||
** A single SQLite connection (an sqlite3* object) or an SQLite statement
|
||||
** (an sqlite3_stmt* object) should only be passed to a single interface
|
||||
** function at a time. The connections and statements can be passed from
|
||||
** any thread to any of the functions listed in the second group above as
|
||||
** long as the same connection is not in use by two threads at once and
|
||||
** as long as SQLITE_ENABLE_MEMORY_MANAGEMENT is not defined. Additional
|
||||
** information about the SQLITE_ENABLE_MEMORY_MANAGEMENT constraint is
|
||||
** below.
|
||||
**
|
||||
** The busy handler for all database connections should remain turned
|
||||
** off. That means that any lock contention will cause the associated
|
||||
** sqlite3_client_step() call to return immediately with an SQLITE_BUSY
|
||||
** error code. If a busy handler is enabled and lock contention occurs,
|
||||
** then the entire server thread will block. This will cause not only
|
||||
** the requesting client to block but every other database client as
|
||||
** well. It is possible to enhance the code below so that lock
|
||||
** contention will cause the message to be placed back on the top of
|
||||
** the queue to be tried again later. But such enhanced processing is
|
||||
** not included here, in order to keep the example simple.
|
||||
**
|
||||
** This example code assumes the use of pthreads. Pthreads
|
||||
** implementations are available for windows. (See, for example
|
||||
** http://sourceware.org/pthreads-win32/announcement.html.) Or, you
|
||||
** can translate the locking and thread synchronization code to use
|
||||
** windows primitives easily enough. The details are left as an
|
||||
** exercise to the reader.
|
||||
**
|
||||
**** Restrictions Associated With SQLITE_ENABLE_MEMORY_MANAGEMENT ****
|
||||
**
|
||||
** If you compile with SQLITE_ENABLE_MEMORY_MANAGEMENT defined, then
|
||||
** SQLite includes code that tracks how much memory is being used by
|
||||
** each thread. These memory counts can become confused if memory
|
||||
** is allocated by one thread and then freed by another. For that
|
||||
** reason, when SQLITE_ENABLE_MEMORY_MANAGEMENT is used, all operations
|
||||
** that might allocate or free memory should be performanced in the same
|
||||
** thread that originally created the database connection. In that case,
|
||||
** many of the operations that are listed above as safe to be performed
|
||||
** in separate threads would need to be sent over to the server to be
|
||||
** done there. If SQLITE_ENABLE_MEMORY_MANAGEMENT is defined, then
|
||||
** the following functions can be used safely from different threads
|
||||
** without messing up the allocation counts:
|
||||
**
|
||||
** sqlite3_bind_parameter_name
|
||||
** sqlite3_bind_parameter_index
|
||||
** sqlite3_changes
|
||||
** sqlite3_column_blob
|
||||
** sqlite3_column_count
|
||||
** sqlite3_complete
|
||||
** sqlite3_data_count
|
||||
** sqlite3_db_handle
|
||||
** sqlite3_errcode
|
||||
** sqlite3_errmsg
|
||||
** sqlite3_last_insert_rowid
|
||||
** sqlite3_total_changes
|
||||
**
|
||||
** The remaining functions are not thread-safe when memory management
|
||||
** is enabled. So one would have to define some new interface routines
|
||||
** along the following lines:
|
||||
**
|
||||
** sqlite3_client_bind_*
|
||||
** sqlite3_client_clear_bindings
|
||||
** sqlite3_client_column_*
|
||||
** sqlite3_client_create_collation
|
||||
** sqlite3_client_create_function
|
||||
** sqlite3_client_transfer_bindings
|
||||
**
|
||||
** The example code in this file is intended for use with memory
|
||||
** management turned off. So the implementation of these additional
|
||||
** client interfaces is left as an exercise to the reader.
|
||||
**
|
||||
** It may seem surprising to the reader that the list of safe functions
|
||||
** above does not include things like sqlite3_bind_int() or
|
||||
** sqlite3_column_int(). But those routines might, in fact, allocate
|
||||
** or deallocate memory. In the case of sqlite3_bind_int(), if the
|
||||
** parameter was previously bound to a string that string might need
|
||||
** to be deallocated before the new integer value is inserted. In
|
||||
** the case of sqlite3_column_int(), the value of the column might be
|
||||
** a UTF-16 string which will need to be converted to UTF-8 then into
|
||||
** an integer.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Only compile the code in this file on UNIX with a THREADSAFE build
|
||||
** and only if the SQLITE_SERVER macro is defined.
|
||||
*/
|
||||
#ifdef SQLITE_SERVER
|
||||
#if defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE
|
||||
|
||||
/*
|
||||
** We require only pthreads and the public interface of SQLite.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Messages are passed from client to server and back again as
|
||||
** instances of the following structure.
|
||||
*/
|
||||
typedef struct SqlMessage SqlMessage;
|
||||
struct SqlMessage {
|
||||
int op; /* Opcode for the message */
|
||||
sqlite3 *pDb; /* The SQLite connection */
|
||||
sqlite3_stmt *pStmt; /* A specific statement */
|
||||
int errCode; /* Error code returned */
|
||||
const char *zIn; /* Input filename or SQL statement */
|
||||
int nByte; /* Size of the zIn parameter for prepare() */
|
||||
const char *zOut; /* Tail of the SQL statement */
|
||||
SqlMessage *pNext; /* Next message in the queue */
|
||||
SqlMessage *pPrev; /* Previous message in the queue */
|
||||
pthread_mutex_t clientMutex; /* Hold this mutex to access the message */
|
||||
pthread_cond_t clientWakeup; /* Signal to wake up the client */
|
||||
};
|
||||
|
||||
/*
|
||||
** Legal values for SqlMessage.op
|
||||
*/
|
||||
#define MSG_Open 1 /* sqlite3_open(zIn, &pDb) */
|
||||
#define MSG_Prepare 2 /* sqlite3_prepare(pDb, zIn, nByte, &pStmt, &zOut) */
|
||||
#define MSG_Step 3 /* sqlite3_step(pStmt) */
|
||||
#define MSG_Reset 4 /* sqlite3_reset(pStmt) */
|
||||
#define MSG_Finalize 5 /* sqlite3_finalize(pStmt) */
|
||||
#define MSG_Close 6 /* sqlite3_close(pDb) */
|
||||
#define MSG_Done 7 /* Server has finished with this message */
|
||||
|
||||
|
||||
/*
|
||||
** State information about the server is stored in a static variable
|
||||
** named "g" as follows:
|
||||
*/
|
||||
static struct ServerState {
|
||||
pthread_mutex_t queueMutex; /* Hold this mutex to access the msg queue */
|
||||
pthread_mutex_t serverMutex; /* Held by the server while it is running */
|
||||
pthread_cond_t serverWakeup; /* Signal this condvar to wake up the server */
|
||||
volatile int serverHalt; /* Server halts itself when true */
|
||||
SqlMessage *pQueueHead; /* Head of the message queue */
|
||||
SqlMessage *pQueueTail; /* Tail of the message queue */
|
||||
} g = {
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_MUTEX_INITIALIZER,
|
||||
PTHREAD_COND_INITIALIZER,
|
||||
};
|
||||
|
||||
/*
|
||||
** Send a message to the server. Block until we get a reply.
|
||||
**
|
||||
** The mutex and condition variable in the message are uninitialized
|
||||
** when this routine is called. This routine takes care of
|
||||
** initializing them and destroying them when it has finished.
|
||||
*/
|
||||
static void sendToServer(SqlMessage *pMsg){
|
||||
/* Initialize the mutex and condition variable on the message
|
||||
*/
|
||||
pthread_mutex_init(&pMsg->clientMutex, 0);
|
||||
pthread_cond_init(&pMsg->clientWakeup, 0);
|
||||
|
||||
/* Add the message to the head of the server's message queue.
|
||||
*/
|
||||
pthread_mutex_lock(&g.queueMutex);
|
||||
pMsg->pNext = g.pQueueHead;
|
||||
if( g.pQueueHead==0 ){
|
||||
g.pQueueTail = pMsg;
|
||||
}else{
|
||||
g.pQueueHead->pPrev = pMsg;
|
||||
}
|
||||
pMsg->pPrev = 0;
|
||||
g.pQueueHead = pMsg;
|
||||
pthread_mutex_unlock(&g.queueMutex);
|
||||
|
||||
/* Signal the server that the new message has be queued, then
|
||||
** block waiting for the server to process the message.
|
||||
*/
|
||||
pthread_mutex_lock(&pMsg->clientMutex);
|
||||
pthread_cond_signal(&g.serverWakeup);
|
||||
while( pMsg->op!=MSG_Done ){
|
||||
pthread_cond_wait(&pMsg->clientWakeup, &pMsg->clientMutex);
|
||||
}
|
||||
pthread_mutex_unlock(&pMsg->clientMutex);
|
||||
|
||||
/* Destroy the mutex and condition variable of the message.
|
||||
*/
|
||||
pthread_mutex_destroy(&pMsg->clientMutex);
|
||||
pthread_cond_destroy(&pMsg->clientWakeup);
|
||||
}
|
||||
|
||||
/*
|
||||
** The following 6 routines are client-side implementations of the
|
||||
** core SQLite interfaces:
|
||||
**
|
||||
** sqlite3_open
|
||||
** sqlite3_prepare
|
||||
** sqlite3_step
|
||||
** sqlite3_reset
|
||||
** sqlite3_finalize
|
||||
** sqlite3_close
|
||||
**
|
||||
** Clients should use the following client-side routines instead of
|
||||
** the core routines above.
|
||||
**
|
||||
** sqlite3_client_open
|
||||
** sqlite3_client_prepare
|
||||
** sqlite3_client_step
|
||||
** sqlite3_client_reset
|
||||
** sqlite3_client_finalize
|
||||
** sqlite3_client_close
|
||||
**
|
||||
** Each of these routines creates a message for the desired operation,
|
||||
** sends that message to the server, waits for the server to process
|
||||
** then message and return a response.
|
||||
*/
|
||||
int sqlite3_client_open(const char *zDatabaseName, sqlite3 **ppDb){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Open;
|
||||
msg.zIn = zDatabaseName;
|
||||
sendToServer(&msg);
|
||||
*ppDb = msg.pDb;
|
||||
return msg.errCode;
|
||||
}
|
||||
int sqlite3_client_prepare(
|
||||
sqlite3 *pDb,
|
||||
const char *zSql,
|
||||
int nByte,
|
||||
sqlite3_stmt **ppStmt,
|
||||
const char **pzTail
|
||||
){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Prepare;
|
||||
msg.pDb = pDb;
|
||||
msg.zIn = zSql;
|
||||
msg.nByte = nByte;
|
||||
sendToServer(&msg);
|
||||
*ppStmt = msg.pStmt;
|
||||
if( pzTail ) *pzTail = msg.zOut;
|
||||
return msg.errCode;
|
||||
}
|
||||
int sqlite3_client_step(sqlite3_stmt *pStmt){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Step;
|
||||
msg.pStmt = pStmt;
|
||||
sendToServer(&msg);
|
||||
return msg.errCode;
|
||||
}
|
||||
int sqlite3_client_reset(sqlite3_stmt *pStmt){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Reset;
|
||||
msg.pStmt = pStmt;
|
||||
sendToServer(&msg);
|
||||
return msg.errCode;
|
||||
}
|
||||
int sqlite3_client_finalize(sqlite3_stmt *pStmt){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Finalize;
|
||||
msg.pStmt = pStmt;
|
||||
sendToServer(&msg);
|
||||
return msg.errCode;
|
||||
}
|
||||
int sqlite3_client_close(sqlite3 *pDb){
|
||||
SqlMessage msg;
|
||||
msg.op = MSG_Close;
|
||||
msg.pDb = pDb;
|
||||
sendToServer(&msg);
|
||||
return msg.errCode;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine implements the server. To start the server, first
|
||||
** make sure g.serverHalt is false, then create a new detached thread
|
||||
** on this procedure. See the sqlite3_server_start() routine below
|
||||
** for an example. This procedure loops until g.serverHalt becomes
|
||||
** true.
|
||||
*/
|
||||
void *sqlite3_server(void *NotUsed){
|
||||
sqlite3_enable_shared_cache(1);
|
||||
if( pthread_mutex_trylock(&g.serverMutex) ){
|
||||
sqlite3_enable_shared_cache(0);
|
||||
return 0; /* Another server is already running */
|
||||
}
|
||||
while( !g.serverHalt ){
|
||||
SqlMessage *pMsg;
|
||||
|
||||
/* Remove the last message from the message queue.
|
||||
*/
|
||||
pthread_mutex_lock(&g.queueMutex);
|
||||
while( g.pQueueTail==0 && g.serverHalt==0 ){
|
||||
pthread_cond_wait(&g.serverWakeup, &g.queueMutex);
|
||||
}
|
||||
pMsg = g.pQueueTail;
|
||||
if( pMsg ){
|
||||
if( pMsg->pPrev ){
|
||||
pMsg->pPrev->pNext = 0;
|
||||
}else{
|
||||
g.pQueueHead = 0;
|
||||
}
|
||||
g.pQueueTail = pMsg->pPrev;
|
||||
}
|
||||
pthread_mutex_unlock(&g.queueMutex);
|
||||
if( pMsg==0 ) break;
|
||||
|
||||
/* Process the message just removed
|
||||
*/
|
||||
pthread_mutex_lock(&pMsg->clientMutex);
|
||||
switch( pMsg->op ){
|
||||
case MSG_Open: {
|
||||
pMsg->errCode = sqlite3_open(pMsg->zIn, &pMsg->pDb);
|
||||
break;
|
||||
}
|
||||
case MSG_Prepare: {
|
||||
pMsg->errCode = sqlite3_prepare(pMsg->pDb, pMsg->zIn, pMsg->nByte,
|
||||
&pMsg->pStmt, &pMsg->zOut);
|
||||
break;
|
||||
}
|
||||
case MSG_Step: {
|
||||
pMsg->errCode = sqlite3_step(pMsg->pStmt);
|
||||
break;
|
||||
}
|
||||
case MSG_Reset: {
|
||||
pMsg->errCode = sqlite3_reset(pMsg->pStmt);
|
||||
break;
|
||||
}
|
||||
case MSG_Finalize: {
|
||||
pMsg->errCode = sqlite3_finalize(pMsg->pStmt);
|
||||
break;
|
||||
}
|
||||
case MSG_Close: {
|
||||
pMsg->errCode = sqlite3_close(pMsg->pDb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal the client that the message has been processed.
|
||||
*/
|
||||
pMsg->op = MSG_Done;
|
||||
pthread_mutex_unlock(&pMsg->clientMutex);
|
||||
pthread_cond_signal(&pMsg->clientWakeup);
|
||||
}
|
||||
pthread_mutex_unlock(&g.serverMutex);
|
||||
sqlite3_thread_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Start a server thread if one is not already running. If there
|
||||
** is aleady a server thread running, the new thread will quickly
|
||||
** die and this routine is effectively a no-op.
|
||||
*/
|
||||
void sqlite3_server_start(void){
|
||||
pthread_t x;
|
||||
int rc;
|
||||
g.serverHalt = 0;
|
||||
rc = pthread_create(&x, 0, sqlite3_server, 0);
|
||||
if( rc==0 ){
|
||||
pthread_detach(x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If a server thread is running, then stop it. If no server is
|
||||
** running, this routine is effectively a no-op.
|
||||
**
|
||||
** This routine returns immediately without waiting for the server
|
||||
** thread to stop. But be assured that the server will eventually stop.
|
||||
*/
|
||||
void sqlite3_server_stop(void){
|
||||
g.serverHalt = 1;
|
||||
pthread_cond_broadcast(&g.serverWakeup);
|
||||
}
|
||||
|
||||
#endif /* defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE */
|
||||
#endif /* defined(SQLITE_SERVER) */
|
||||
+11
-1
@@ -12,7 +12,7 @@
|
||||
# focus of this script is testing the ATTACH and DETACH commands
|
||||
# and related functionality.
|
||||
#
|
||||
# $Id: attach2.test,v 1.35 2006/01/03 00:33:50 drh Exp $
|
||||
# $Id: attach2.test,v 1.35.4.1 2007/08/14 13:20:28 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -212,6 +212,15 @@ do_test attach2-4.4 {
|
||||
lock_status 4.4.1 db {main shared temp closed file2 unlocked}
|
||||
lock_status 4.4.2 db2 {main unlocked temp closed file2 unlocked}
|
||||
|
||||
# We have to make sure that the cache_size and the soft_heap_limit
|
||||
# are large enough to hold the entire change in memory. If either
|
||||
# is set too small, then changes will spill to the database, forcing
|
||||
# a reserved lock to promote to exclusive. That will mess up our
|
||||
# test results.
|
||||
|
||||
set soft_limit [sqlite3_soft_heap_limit 0]
|
||||
|
||||
|
||||
do_test attach2-4.5 {
|
||||
# Handle 'db2' reserves file2.
|
||||
execsql {BEGIN} db2
|
||||
@@ -314,6 +323,7 @@ do_test attach2-4.15 {
|
||||
db close
|
||||
db2 close
|
||||
file delete -force test2.db
|
||||
sqlite3_soft_heap_limit $soft_limit
|
||||
|
||||
# These tests - attach2-5.* - check that the master journal file is deleted
|
||||
# correctly when a multi-file transaction is committed or rolled back.
|
||||
|
||||
+11
-1
@@ -13,13 +13,21 @@
|
||||
# particular the behavior of sqlite3_step() when trying to commit
|
||||
# with lock contention.
|
||||
#
|
||||
# $Id: capi3b.test,v 1.3 2006/01/03 00:33:50 drh Exp $
|
||||
# $Id: capi3b.test,v 1.3.4.1 2007/08/14 13:20:28 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
# These tests depend on the pager holding changes in cache
|
||||
# until it is time to commit. But that won't happen if the
|
||||
# soft-heap-limit is set too low. So disable the soft heap limit
|
||||
# for the duration of this test.
|
||||
#
|
||||
sqlite3_soft_heap_limit 0
|
||||
|
||||
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
sqlite3 db2 test.db
|
||||
set DB2 [sqlite3_connection_pointer db2]
|
||||
@@ -132,4 +140,6 @@ do_test capi3b-2.12 {
|
||||
} {1 2 3 4}
|
||||
|
||||
catch {db2 close}
|
||||
|
||||
sqlite3_soft_heap_limit $soft_limit
|
||||
finish_test
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
** This program tests the ability of SQLite database to recover from a crash.
|
||||
** This program runs under Unix only, but the results are applicable to all
|
||||
** systems.
|
||||
**
|
||||
** The main process first constructs a test database, then starts creating
|
||||
** subprocesses that write to that database. Each subprocess is killed off,
|
||||
** without a chance to clean up its database connection, after a random
|
||||
** delay. This killing of the subprocesses simulates a crash or power
|
||||
** failure. The next subprocess to open the database should rollback
|
||||
** whatever operation was in process at the time of the simulated crash.
|
||||
**
|
||||
** If any problems are encountered, an error is reported and the test stops.
|
||||
** If no problems are seen after a large number of tests, we assume that
|
||||
** the rollback mechanism is working.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include "sqlite.h"
|
||||
|
||||
static void do_some_sql(int parent){
|
||||
char *zErr;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite *db;
|
||||
int cnt = 0;
|
||||
static char zBig[] =
|
||||
"-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
if( access("./test.db-journal",0)==0 ){
|
||||
/*printf("pid %d: journal exists. rollback will be required\n",getpid());*/ unlink("test.db-saved");
|
||||
system("cp test.db test.db-saved");
|
||||
unlink("test.db-journal-saved");
|
||||
system("cp test.db-journal test.db-journal-saved");
|
||||
}
|
||||
db = sqlite_open("./test.db", 0, &zErr);
|
||||
if( db==0 ){
|
||||
printf("ERROR: %s\n", zErr);
|
||||
if( strcmp(zErr,"database disk image is malformed")==0 ){
|
||||
kill(parent, SIGKILL);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
srand(getpid());
|
||||
while( rc==SQLITE_OK ){
|
||||
cnt++;
|
||||
rc = sqlite_exec_printf(db,
|
||||
"INSERT INTO t1 VALUES(%d,'%d%s')", 0, 0, &zErr,
|
||||
rand(), rand(), zBig);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
printf("ERROR #%d: %s\n", rc, zErr);
|
||||
if( rc==SQLITE_CORRUPT ){
|
||||
kill(parent, SIGKILL);
|
||||
}
|
||||
}
|
||||
printf("pid %d: cnt=%d\n", getpid(), cnt);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int i;
|
||||
sqlite *db;
|
||||
char *zErr;
|
||||
int status;
|
||||
int parent = getpid();
|
||||
|
||||
unlink("test.db");
|
||||
unlink("test.db-journal");
|
||||
db = sqlite_open("test.db", 0, &zErr);
|
||||
if( db==0 ){
|
||||
printf("Cannot initialize: %s\n", zErr);
|
||||
return 1;
|
||||
}
|
||||
sqlite_exec(db, "CREATE TABLE t1(a,b)", 0, 0, 0);
|
||||
sqlite_close(db);
|
||||
for(i=0; i<10000; i++){
|
||||
int pid = fork();
|
||||
if( pid==0 ){
|
||||
sched_yield();
|
||||
do_some_sql(parent);
|
||||
return 0;
|
||||
}
|
||||
printf("test %d, pid=%d\n", i, pid);
|
||||
usleep(rand()%10000 + 1000);
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+29
-1
@@ -11,7 +11,7 @@
|
||||
# This file implements some common TCL routines used for regression
|
||||
# testing the SQLite library
|
||||
#
|
||||
# $Id: tester.tcl,v 1.79 2007/04/19 12:30:54 drh Exp $
|
||||
# $Id: tester.tcl,v 1.79.2.1 2007/08/14 13:20:28 drh Exp $
|
||||
|
||||
# Make sure tclsqlite3 was compiled correctly. Abort now with an
|
||||
# error message if not.
|
||||
@@ -43,6 +43,26 @@ if {[sqlite3 -tcl-uses-utf]} {
|
||||
set tcl_precision 15
|
||||
set sqlite_pending_byte 0x0010000
|
||||
|
||||
#
|
||||
# Check the command-line arguments for a default soft-heap-limit.
|
||||
# Store this default value in the global variable ::soft_limit and
|
||||
# update the soft-heap-limit each time this script is run. In that
|
||||
# way if an individual test file changes the soft-heap-limit, it
|
||||
# will be reset at the start of the next test file.
|
||||
#
|
||||
if {![info exists soft_limit]} {
|
||||
set soft_limit 0
|
||||
for {set i 0} {$i<[llength $argv]} {incr i} {
|
||||
if {[regexp {^--soft-heap-limit=(.+)$} [lindex $argv $i] all value]} {
|
||||
if {$value!="off"} {
|
||||
set soft_limit $value
|
||||
}
|
||||
set argv [lreplace $argv $i $i]
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_soft_heap_limit $soft_limit
|
||||
|
||||
# Use the pager codec if it is available
|
||||
#
|
||||
if {[sqlite3 -has-codec] && [info command sqlite_orig]==""} {
|
||||
@@ -178,6 +198,14 @@ proc finalize_testing {} {
|
||||
sqlite3 db {}
|
||||
# sqlite3_clear_tsd_memdebug
|
||||
db close
|
||||
set heaplimit [sqlite3_soft_heap_limit]
|
||||
if {$heaplimit!=$::soft_limit} {
|
||||
puts "soft-heap-limit changed by this script\
|
||||
from $::soft_limit to $heaplimit"
|
||||
} elseif {$heaplimit!="" && $heaplimit>0} {
|
||||
puts "soft-heap-limit set to $heaplimit"
|
||||
}
|
||||
sqlite3_soft_heap_limit 0
|
||||
if {$::sqlite3_tsd_count} {
|
||||
puts "Thread-specific data leak: $::sqlite3_tsd_count instances"
|
||||
incr nErr
|
||||
|
||||
-1796
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user