mirror of
https://github.com/isar/rusqlite.git
synced 2025-04-02 04:12:59 +08:00
Merge remote-tracking branch 'origin/master' into sub_type
This commit is contained in:
commit
d0f8dda648
@ -43,6 +43,8 @@ collation = []
|
||||
functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
|
||||
# sqlite3_log: 3.6.23 (2010-03-09)
|
||||
trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
|
||||
# sqlite3_db_release_memory: 3.7.10 (2012-01-16)
|
||||
release_memory = ["libsqlite3-sys/min_sqlite_version_3_7_16"]
|
||||
bundled = ["libsqlite3-sys/bundled", "modern_sqlite"]
|
||||
bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"]
|
||||
bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"]
|
||||
@ -118,16 +120,15 @@ url = { version = "2.1", optional = true }
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
fallible-iterator = "0.2"
|
||||
fallible-streaming-iterator = "0.1"
|
||||
memchr = "2.3"
|
||||
uuid = { version = "0.8", optional = true }
|
||||
uuid = { version = "1.0", optional = true }
|
||||
smallvec = "1.6.1"
|
||||
|
||||
[dev-dependencies]
|
||||
doc-comment = "0.3"
|
||||
tempfile = "3.1.0"
|
||||
lazy_static = "1.4"
|
||||
regex = "1.3"
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
regex = "1.5.5"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
unicase = "2.6.0"
|
||||
# Use `bencher` over criterion because it builds much faster and we don't have
|
||||
# many benchmarks
|
||||
|
@ -136,6 +136,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
|
||||
* `extra_check` fail when a query passed to execute is readonly or has a column count > 0.
|
||||
* `column_decltype` provides `columns()` method for Statements and Rows; omit if linking to a version of SQLite/SQLCipher compiled with `-DSQLITE_OMIT_DECLTYPE`.
|
||||
* `collation` exposes [`sqlite3_create_collation_v2`](https://sqlite.org/c3ref/create_collation.html).
|
||||
* `winsqlite3` allows linking against the SQLite present in newer versions of Windows
|
||||
|
||||
## Notes on building rusqlite and libsqlite3-sys
|
||||
|
||||
@ -155,7 +156,7 @@ You can adjust this behavior in a number of ways:
|
||||
version = "0.27.0"
|
||||
features = ["bundled"]
|
||||
```
|
||||
* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.)
|
||||
* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE3_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.)
|
||||
* When using `bundled-sqlcipher` (and not also using `bundled-sqlcipher-vendored-openssl`), `libsqlite3-sys` will need to
|
||||
link against crypto libraries on the system. If the build script can find a `libcrypto` from OpenSSL or LibreSSL (it will consult `OPENSSL_LIB_DIR`/`OPENSSL_INCLUDE_DIR` and `OPENSSL_DIR` environment variables), it will use that. If building on and for Macs, and none of those variables are set, it will use the system's SecurityFramework instead.
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* automatically generated by rust-bindgen 0.59.2 */
|
||||
|
||||
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.38.2\0";
|
||||
pub const SQLITE_VERSION_NUMBER: i32 = 3038002;
|
||||
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.38.3\0";
|
||||
pub const SQLITE_VERSION_NUMBER: i32 = 3038003;
|
||||
pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
|
||||
b"2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f\0";
|
||||
b"2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4\0";
|
||||
pub const SQLITE_OK: i32 = 0;
|
||||
pub const SQLITE_ERROR: i32 = 1;
|
||||
pub const SQLITE_INTERNAL: i32 = 2;
|
||||
|
128
libsqlite3-sys/sqlite3/sqlite3.c
vendored
128
libsqlite3-sys/sqlite3/sqlite3.c
vendored
@ -1,6 +1,6 @@
|
||||
/******************************************************************************
|
||||
** This file is an amalgamation of many separate C source files from SQLite
|
||||
** version 3.38.2. By combining all the individual C code files into this
|
||||
** version 3.38.3. By combining all the individual C code files into this
|
||||
** single large file, the entire code can be compiled as a single translation
|
||||
** unit. This allows many compilers to do optimizations that would not be
|
||||
** possible if the files were compiled separately. Performance improvements
|
||||
@ -452,9 +452,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.38.2"
|
||||
#define SQLITE_VERSION_NUMBER 3038002
|
||||
#define SQLITE_SOURCE_ID "2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f"
|
||||
#define SQLITE_VERSION "3.38.3"
|
||||
#define SQLITE_VERSION_NUMBER 3038003
|
||||
#define SQLITE_SOURCE_ID "2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@ -19929,6 +19929,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
|
||||
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
|
||||
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
|
||||
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
|
||||
SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*);
|
||||
#ifdef SQLITE_ENABLE_CURSOR_HINTS
|
||||
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
|
||||
#endif
|
||||
@ -29337,8 +29338,9 @@ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const cha
|
||||
** Free any prior content in *pz and replace it with a copy of zNew.
|
||||
*/
|
||||
SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
|
||||
char *z = sqlite3DbStrDup(db, zNew);
|
||||
sqlite3DbFree(db, *pz);
|
||||
*pz = sqlite3DbStrDup(db, zNew);
|
||||
*pz = z;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -67764,6 +67766,8 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
|
||||
** fragmented bytes within the page. */
|
||||
memcpy(&aData[iAddr], &aData[pc], 2);
|
||||
aData[hdr+7] += (u8)x;
|
||||
testcase( pc+x>maxPC );
|
||||
return &aData[pc];
|
||||
}else if( x+pc > maxPC ){
|
||||
/* This slot extends off the end of the usable part of the page */
|
||||
*pRc = SQLITE_CORRUPT_PAGE(pPg);
|
||||
@ -71963,7 +71967,7 @@ SQLITE_PRIVATE int sqlite3BtreeIndexMoveto(
|
||||
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
|
||||
assert( pPage->isInit );
|
||||
if( pPage->leaf ){
|
||||
assert( pCur->ix<pCur->pPage->nCell );
|
||||
assert( pCur->ix<pCur->pPage->nCell || CORRUPT_DB );
|
||||
pCur->ix = (u16)idx;
|
||||
*pRes = c;
|
||||
rc = SQLITE_OK;
|
||||
@ -74487,7 +74491,7 @@ static int balance_nonroot(
|
||||
iOvflSpace += sz;
|
||||
assert( sz<=pBt->maxLocal+23 );
|
||||
assert( iOvflSpace <= (int)pBt->pageSize );
|
||||
for(k=0; b.ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
|
||||
for(k=0; b.ixNx[k]<=j && ALWAYS(k<NB*2); k++){}
|
||||
pSrcEnd = b.apEnd[k];
|
||||
if( SQLITE_WITHIN(pSrcEnd, pCell, pCell+sz) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
@ -78052,7 +78056,11 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
|
||||
assert( !sqlite3VdbeMemIsRowSet(pMem) );
|
||||
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|
||||
|| desiredEnc==SQLITE_UTF16BE );
|
||||
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
|
||||
if( !(pMem->flags&MEM_Str) ){
|
||||
pMem->enc = desiredEnc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( pMem->enc==desiredEnc ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
@ -104759,6 +104767,38 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
|
||||
return exprIsConst(p, 3, iCur);
|
||||
}
|
||||
|
||||
/*
|
||||
** Check pExpr to see if it is an invariant constraint on data source pSrc.
|
||||
** This is an optimization. False negatives will perhaps cause slower
|
||||
** queries, but false positives will yield incorrect answers. So when in
|
||||
** double, return 0.
|
||||
**
|
||||
** To be an invariant constraint, the following must be true:
|
||||
**
|
||||
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
|
||||
**
|
||||
** (2) pExpr cannot use subqueries or non-deterministic functions.
|
||||
**
|
||||
** (*) ** Not applicable to this branch **
|
||||
**
|
||||
** (4) If pSrc is the right operand of a LEFT JOIN, then...
|
||||
** (4a) pExpr must come from an ON clause..
|
||||
** (4b) and specifically the ON clause associated with the LEFT JOIN.
|
||||
**
|
||||
** (5) If pSrc is not the right operand of a LEFT JOIN or the left
|
||||
** operand of a RIGHT JOIN, then pExpr must be from the WHERE
|
||||
** clause, not an ON clause.
|
||||
*/
|
||||
SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){
|
||||
if( pSrc->fg.jointype & JT_LEFT ){
|
||||
if( !ExprHasProperty(pExpr, EP_FromJoin) ) return 0; /* rule (4a) */
|
||||
if( pExpr->w.iRightJoinTable!=pSrc->iCursor ) return 0; /* rule (4b) */
|
||||
}else{
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; /* rule (5) */
|
||||
}
|
||||
return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy().
|
||||
@ -139042,8 +139082,7 @@ static int pushDownWhereTerms(
|
||||
Parse *pParse, /* Parse context (for malloc() and error reporting) */
|
||||
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
|
||||
Expr *pWhere, /* The WHERE clause of the outer query */
|
||||
int iCursor, /* Cursor number of the subquery */
|
||||
int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
|
||||
SrcItem *pSrc /* The subquery term of the outer FROM clause */
|
||||
){
|
||||
Expr *pNew;
|
||||
int nChng = 0;
|
||||
@ -139078,10 +139117,11 @@ static int pushDownWhereTerms(
|
||||
return 0; /* restriction (3) */
|
||||
}
|
||||
while( pWhere->op==TK_AND ){
|
||||
nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight,
|
||||
iCursor, isLeftJoin);
|
||||
nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc);
|
||||
pWhere = pWhere->pLeft;
|
||||
}
|
||||
|
||||
#if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */
|
||||
if( isLeftJoin
|
||||
&& (ExprHasProperty(pWhere,EP_FromJoin)==0
|
||||
|| pWhere->w.iRightJoinTable!=iCursor)
|
||||
@ -139093,7 +139133,9 @@ static int pushDownWhereTerms(
|
||||
){
|
||||
return 0; /* restriction (5) */
|
||||
}
|
||||
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
|
||||
#endif
|
||||
|
||||
if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){
|
||||
nChng++;
|
||||
pSubq->selFlags |= SF_PushDown;
|
||||
while( pSubq ){
|
||||
@ -139101,8 +139143,8 @@ static int pushDownWhereTerms(
|
||||
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
|
||||
unsetJoinExpr(pNew, -1);
|
||||
x.pParse = pParse;
|
||||
x.iTable = iCursor;
|
||||
x.iNewTable = iCursor;
|
||||
x.iTable = pSrc->iCursor;
|
||||
x.iNewTable = pSrc->iCursor;
|
||||
x.isLeftJoin = 0;
|
||||
x.pEList = pSubq->pEList;
|
||||
pNew = substExpr(&x, pNew);
|
||||
@ -140884,8 +140926,7 @@ SQLITE_PRIVATE int sqlite3Select(
|
||||
if( OptimizationEnabled(db, SQLITE_PushDown)
|
||||
&& (pItem->fg.isCte==0
|
||||
|| (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
|
||||
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
|
||||
(pItem->fg.jointype & JT_OUTER)!=0)
|
||||
&& pushDownWhereTerms(pParse, pSub, p->pWhere, pItem)
|
||||
){
|
||||
#if SELECTTRACE_ENABLED
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
@ -152810,8 +152851,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
|
||||
** WHERE clause (or the ON clause of a LEFT join) that constrain which
|
||||
** rows of the target table (pSrc) that can be used. */
|
||||
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
|
||||
&& ((pSrc->fg.jointype&JT_LEFT)==0 || ExprHasProperty(pExpr,EP_FromJoin))
|
||||
&& sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)
|
||||
&& sqlite3ExprIsTableConstraint(pExpr, pSrc)
|
||||
){
|
||||
pPartial = sqlite3ExprAnd(pParse, pPartial,
|
||||
sqlite3ExprDup(pParse->db, pExpr, 0));
|
||||
@ -153050,7 +153090,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
|
||||
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
|
||||
&& sqlite3ExprIsTableConstant(pExpr, iCur)
|
||||
&& sqlite3ExprIsTableConstraint(pExpr, pItem)
|
||||
){
|
||||
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
@ -159970,7 +160010,7 @@ static void windowAggStep(
|
||||
|
||||
for(iEnd=sqlite3VdbeCurrentAddr(v); iOp<iEnd; iOp++){
|
||||
VdbeOp *pOp = sqlite3VdbeGetOp(v, iOp);
|
||||
if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
|
||||
if( pOp->opcode==OP_Column && pOp->p1==pMWin->iEphCsr ){
|
||||
pOp->p1 = csr;
|
||||
}
|
||||
}
|
||||
@ -194288,14 +194328,15 @@ static JsonNode *jsonLookupStep(
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
testcase( nKey==0 );
|
||||
}else{
|
||||
zKey = zPath;
|
||||
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
|
||||
nKey = i;
|
||||
}
|
||||
if( nKey==0 ){
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
if( nKey==0 ){
|
||||
*pzErr = zPath;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
j = 1;
|
||||
for(;;){
|
||||
@ -195443,6 +195484,33 @@ static int jsonEachNext(sqlite3_vtab_cursor *cur){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Append an object label to the JSON Path being constructed
|
||||
** in pStr.
|
||||
*/
|
||||
static void jsonAppendObjectPathElement(
|
||||
JsonString *pStr,
|
||||
JsonNode *pNode
|
||||
){
|
||||
int jj, nn;
|
||||
const char *z;
|
||||
assert( pNode->eType==JSON_STRING );
|
||||
assert( pNode->jnFlags & JNODE_LABEL );
|
||||
assert( pNode->eU==1 );
|
||||
z = pNode->u.zJContent;
|
||||
nn = pNode->n;
|
||||
assert( nn>=2 );
|
||||
assert( z[0]=='"' );
|
||||
assert( z[nn-1]=='"' );
|
||||
if( nn>2 && sqlite3Isalpha(z[1]) ){
|
||||
for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
|
||||
if( jj==nn-1 ){
|
||||
z++;
|
||||
nn -= 2;
|
||||
}
|
||||
}
|
||||
jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
|
||||
}
|
||||
|
||||
/* Append the name of the path for element i to pStr
|
||||
*/
|
||||
static void jsonEachComputePath(
|
||||
@ -195467,10 +195535,7 @@ static void jsonEachComputePath(
|
||||
}else{
|
||||
assert( pUp->eType==JSON_OBJECT );
|
||||
if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
|
||||
assert( pNode->eType==JSON_STRING );
|
||||
assert( pNode->jnFlags & JNODE_LABEL );
|
||||
assert( pNode->eU==1 );
|
||||
jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
|
||||
jsonAppendObjectPathElement(pStr, pNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195541,8 +195606,7 @@ static int jsonEachColumn(
|
||||
if( p->eType==JSON_ARRAY ){
|
||||
jsonPrintf(30, &x, "[%d]", p->iRowid);
|
||||
}else if( p->eType==JSON_OBJECT ){
|
||||
assert( pThis->eU==1 );
|
||||
jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
|
||||
jsonAppendObjectPathElement(&x, pThis);
|
||||
}
|
||||
}
|
||||
jsonResult(&x);
|
||||
@ -234433,7 +234497,7 @@ static void fts5SourceIdFunc(
|
||||
){
|
||||
assert( nArg==0 );
|
||||
UNUSED_PARAM2(nArg, apUnused);
|
||||
sqlite3_result_text(pCtx, "fts5: 2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f", -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(pCtx, "fts5: 2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4", -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
6
libsqlite3-sys/sqlite3/sqlite3.h
vendored
6
libsqlite3-sys/sqlite3/sqlite3.h
vendored
@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.38.2"
|
||||
#define SQLITE_VERSION_NUMBER 3038002
|
||||
#define SQLITE_SOURCE_ID "2022-03-26 13:51:10 d33c709cc0af66bc5b6dc6216eba9f1f0b40960b9ae83694c986fbf4c1d6f08f"
|
||||
#define SQLITE_VERSION "3.38.3"
|
||||
#define SQLITE_VERSION_NUMBER 3038003
|
||||
#define SQLITE_SOURCE_ID "2022-04-27 12:03:15 9547e2c38a1c6f751a77d4d796894dec4dc5d8f5d79b1cd39e1ffc50df7b3be4"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -9,7 +9,7 @@ export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3"
|
||||
export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
|
||||
|
||||
# Download and extract amalgamation
|
||||
SQLITE=sqlite-amalgamation-3380200
|
||||
SQLITE=sqlite-amalgamation-3380300
|
||||
curl -O https://sqlite.org/2022/$SQLITE.zip
|
||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c"
|
||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h"
|
||||
|
12
src/busy.rs
12
src/busy.rs
@ -90,7 +90,7 @@ mod test {
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{Connection, Error, ErrorCode, Result, TransactionBehavior};
|
||||
use crate::{Connection, ErrorCode, Result, TransactionBehavior};
|
||||
|
||||
#[test]
|
||||
fn test_default_busy() -> Result<()> {
|
||||
@ -101,12 +101,10 @@ mod test {
|
||||
let tx1 = db1.transaction_with_behavior(TransactionBehavior::Exclusive)?;
|
||||
let db2 = Connection::open(&path)?;
|
||||
let r: Result<()> = db2.query_row("PRAGMA schema_version", [], |_| unreachable!());
|
||||
match r.unwrap_err() {
|
||||
Error::SqliteFailure(err, _) => {
|
||||
assert_eq!(err.code, ErrorCode::DatabaseBusy);
|
||||
}
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
assert_eq!(
|
||||
r.unwrap_err().sqlite_error_code(),
|
||||
Some(ErrorCode::DatabaseBusy)
|
||||
);
|
||||
tx1.rollback()
|
||||
}
|
||||
|
||||
|
67
src/error.rs
67
src/error.rs
@ -128,6 +128,19 @@ pub enum Error {
|
||||
#[cfg(feature = "blob")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
|
||||
BlobSizeError,
|
||||
/// Error referencing a specific token in the input SQL
|
||||
#[cfg(feature = "modern_sqlite")] // 3.38.0
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
|
||||
SqlInputError {
|
||||
/// error code
|
||||
error: ffi::Error,
|
||||
/// error message
|
||||
msg: String,
|
||||
/// SQL input
|
||||
sql: String,
|
||||
/// byte offset of the start of invalid token
|
||||
offset: c_int,
|
||||
},
|
||||
}
|
||||
|
||||
impl PartialEq for Error {
|
||||
@ -172,6 +185,21 @@ impl PartialEq for Error {
|
||||
}
|
||||
#[cfg(feature = "blob")]
|
||||
(Error::BlobSizeError, Error::BlobSizeError) => true,
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
(
|
||||
Error::SqlInputError {
|
||||
error: e1,
|
||||
msg: m1,
|
||||
sql: s1,
|
||||
offset: o1,
|
||||
},
|
||||
Error::SqlInputError {
|
||||
error: e2,
|
||||
msg: m2,
|
||||
sql: s2,
|
||||
offset: o2,
|
||||
},
|
||||
) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
|
||||
(..) => false,
|
||||
}
|
||||
}
|
||||
@ -281,9 +309,15 @@ impl fmt::Display for Error {
|
||||
#[cfg(feature = "functions")]
|
||||
Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
|
||||
Error::MultipleStatement => write!(f, "Multiple statements provided"),
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
Error::BlobSizeError => "Blob size is insufficient".fmt(f),
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
Error::SqlInputError {
|
||||
ref msg,
|
||||
offset,
|
||||
ref sql,
|
||||
..
|
||||
} => write!(f, "{} in {} at offset {}", msg, sql, offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -331,6 +365,8 @@ impl error::Error for Error {
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
Error::BlobSizeError => None,
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
Error::SqlInputError { ref error, .. } => Some(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -371,6 +407,35 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
|
||||
error_from_sqlite_code(code, message)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0
|
||||
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error {
|
||||
error_from_handle(db, code)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
|
||||
pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error {
|
||||
if db.is_null() {
|
||||
error_from_sqlite_code(code, None)
|
||||
} else {
|
||||
let error = ffi::Error::new(code);
|
||||
let msg = errmsg_to_string(ffi::sqlite3_errmsg(db));
|
||||
if ffi::ErrorCode::Unknown == error.code {
|
||||
let offset = ffi::sqlite3_error_offset(db);
|
||||
if offset >= 0 {
|
||||
return Error::SqlInputError {
|
||||
error,
|
||||
msg,
|
||||
sql: sql.to_owned(),
|
||||
offset,
|
||||
};
|
||||
}
|
||||
}
|
||||
Error::SqliteFailure(error, Some(msg))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(code: c_int) -> Result<()> {
|
||||
if code != crate::ffi::SQLITE_OK {
|
||||
Err(crate::error::error_from_sqlite_code(code, None))
|
||||
|
@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex};
|
||||
use super::ffi;
|
||||
use super::str_for_sqlite;
|
||||
use super::{Connection, InterruptHandle, OpenFlags, Result};
|
||||
use crate::error::{error_from_handle, error_from_sqlite_code, Error};
|
||||
use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
|
||||
use crate::raw_statement::RawStatement;
|
||||
use crate::statement::Statement;
|
||||
use crate::version::version_number;
|
||||
@ -256,7 +256,9 @@ impl InnerConnection {
|
||||
rc
|
||||
};
|
||||
// If there is an error, *ppStmt is set to NULL.
|
||||
self.decode_result(r)?;
|
||||
if r != ffi::SQLITE_OK {
|
||||
return Err(unsafe { error_with_offset(self.db, r, sql) });
|
||||
}
|
||||
// If the input text contains no SQL (if the input is an empty string or a
|
||||
// comment) then *ppStmt is set to NULL.
|
||||
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
|
||||
@ -360,6 +362,12 @@ impl InnerConnection {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "release_memory")]
|
||||
pub fn release_memory(&self) -> Result<()> {
|
||||
self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InnerConnection {
|
||||
|
31
src/lib.rs
31
src/lib.rs
@ -384,9 +384,8 @@ impl Connection {
|
||||
///
|
||||
/// - Open the database for both reading or writing.
|
||||
/// - Create the database if one does not exist at the path.
|
||||
/// - Allow the filename to be interpreted as a URI (see
|
||||
/// <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite> for
|
||||
/// details).
|
||||
/// - Allow the filename to be interpreted as a URI (see <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite>
|
||||
/// for details).
|
||||
/// - Disables the use of a per-connection mutex.
|
||||
///
|
||||
/// Rusqlite enforces thread-safety at compile time, so additional
|
||||
@ -596,6 +595,16 @@ impl Connection {
|
||||
self.path.as_deref()
|
||||
}
|
||||
|
||||
/// Attempts to free as much heap memory as possible from the database
|
||||
/// connection.
|
||||
///
|
||||
/// This calls [`sqlite3_db_release_memory`](https://www.sqlite.org/c3ref/db_release_memory.html).
|
||||
#[inline]
|
||||
#[cfg(feature = "release_memory")]
|
||||
pub fn release_memory(&self) -> Result<()> {
|
||||
self.db.borrow_mut().release_memory()
|
||||
}
|
||||
|
||||
/// Convenience method to prepare and execute a single SQL statement with
|
||||
/// named parameter(s).
|
||||
///
|
||||
@ -1289,7 +1298,7 @@ mod test {
|
||||
let filename = "no_such_file.db";
|
||||
let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY);
|
||||
assert!(result.is_err());
|
||||
let err = result.err().unwrap();
|
||||
let err = result.unwrap_err();
|
||||
if let Error::SqliteFailure(e, Some(msg)) = err {
|
||||
assert_eq!(ErrorCode::CannotOpen, e.code);
|
||||
assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
|
||||
@ -1742,14 +1751,10 @@ mod test {
|
||||
|
||||
let result: Result<Vec<i32>> = stmt.query([])?.map(|r| r.get(0)).collect();
|
||||
|
||||
match result.unwrap_err() {
|
||||
Error::SqliteFailure(err, _) => {
|
||||
assert_eq!(err.code, ErrorCode::OperationInterrupted);
|
||||
}
|
||||
err => {
|
||||
panic!("Unexpected error {}", err);
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
result.unwrap_err().sqlite_error_code(),
|
||||
Some(ErrorCode::OperationInterrupted)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -2093,7 +2098,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "bundled", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.35.0
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
fn test_returning() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
|
||||
|
@ -327,7 +327,7 @@ macro_rules! impl_for_array_ref {
|
||||
// don't really think it matters -- users who hit that can use `params!` anyway.
|
||||
impl_for_array_ref!(
|
||||
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
||||
18 19 20 21 22 23 24 25 26 27 29 30 31 32
|
||||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
||||
);
|
||||
|
||||
/// Adapter type which allows any iterator over [`ToSql`] values to implement
|
||||
|
@ -1535,4 +1535,21 @@ mod test {
|
||||
assert_eq!(0, stmt.is_explain());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
|
||||
fn test_error_offset() -> Result<()> {
|
||||
use crate::ffi::ErrorCode;
|
||||
let db = Connection::open_in_memory()?;
|
||||
let r = db.execute_batch("SELECT CURRENT_TIMESTANP;");
|
||||
assert!(r.is_err());
|
||||
match r.unwrap_err() {
|
||||
Error::SqlInputError { error, offset, .. } => {
|
||||
assert_eq!(error.code, ErrorCode::Unknown);
|
||||
assert_eq!(offset, 7);
|
||||
}
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ pub struct Transaction<'conn> {
|
||||
/// sp.commit()
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Savepoint<'conn> {
|
||||
conn: &'conn Connection,
|
||||
name: String,
|
||||
|
@ -110,7 +110,7 @@ pub struct Null;
|
||||
|
||||
/// SQLite data types.
|
||||
/// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
/// NULL
|
||||
Null,
|
||||
@ -272,85 +272,67 @@ mod test {
|
||||
// check some invalid types
|
||||
|
||||
// 0 is actually a blob (Vec<u8>)
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_int>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_int>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
|
||||
assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
|
||||
assert!(is_invalid_column_type(row.get::<_, i64>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_double>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, String>(0).err().unwrap()
|
||||
row.get::<_, c_double>(0).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, String>(0).unwrap_err()));
|
||||
#[cfg(feature = "time")]
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, time::OffsetDateTime>(0).err().unwrap()
|
||||
row.get::<_, time::OffsetDateTime>(0).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Option<c_int>>(0).err().unwrap()
|
||||
row.get::<_, Option<c_int>>(0).unwrap_err()
|
||||
));
|
||||
|
||||
// 1 is actually a text (String)
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_int>(1).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, c_int>(1).unwrap_err()));
|
||||
assert!(is_invalid_column_type(row.get::<_, i64>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_double>(1).err().unwrap()
|
||||
row.get::<_, c_double>(1).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Vec<u8>>(1).err().unwrap()
|
||||
row.get::<_, Vec<u8>>(1).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Option<c_int>>(1).err().unwrap()
|
||||
row.get::<_, Option<c_int>>(1).unwrap_err()
|
||||
));
|
||||
|
||||
// 2 is actually an integer
|
||||
assert!(is_invalid_column_type(row.get::<_, String>(2).unwrap_err()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, String>(2).err().unwrap()
|
||||
row.get::<_, Vec<u8>>(2).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Vec<u8>>(2).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Option<String>>(2).err().unwrap()
|
||||
row.get::<_, Option<String>>(2).unwrap_err()
|
||||
));
|
||||
|
||||
// 3 is actually a float (c_double)
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_int>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, c_int>(3).unwrap_err()));
|
||||
assert!(is_invalid_column_type(row.get::<_, i64>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get::<_, String>(3).unwrap_err()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, String>(3).err().unwrap()
|
||||
row.get::<_, Vec<u8>>(3).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Vec<u8>>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Option<c_int>>(3).err().unwrap()
|
||||
row.get::<_, Option<c_int>>(3).unwrap_err()
|
||||
));
|
||||
|
||||
// 4 is actually NULL
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_int>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, c_int>(4).unwrap_err()));
|
||||
assert!(is_invalid_column_type(row.get::<_, i64>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, c_double>(4).err().unwrap()
|
||||
row.get::<_, c_double>(4).unwrap_err()
|
||||
));
|
||||
assert!(is_invalid_column_type(row.get::<_, String>(4).unwrap_err()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, String>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, Vec<u8>>(4).err().unwrap()
|
||||
row.get::<_, Vec<u8>>(4).unwrap_err()
|
||||
));
|
||||
#[cfg(feature = "time")]
|
||||
assert!(is_invalid_column_type(
|
||||
row.get::<_, time::OffsetDateTime>(4).err().unwrap()
|
||||
row.get::<_, time::OffsetDateTime>(4).unwrap_err()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user