mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-30 22:11:14 +00:00
Merge branch 'master' of github.com:sqlcipher/sqlcipher into ditto
This commit is contained in:
+8
-1
@@ -88,7 +88,14 @@ int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLef
|
||||
}
|
||||
|
||||
CODEC_TRACE(("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
|
||||
|
||||
|
||||
if( sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){
|
||||
if(ctx) {
|
||||
char *add_random_status = sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlite3Strlen30(zRight)));
|
||||
codec_vdbe_return_static_string(pParse, "cipher_add_random", add_random_status);
|
||||
sqlite3_free(add_random_status);
|
||||
}
|
||||
} else
|
||||
if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){
|
||||
if(ctx){
|
||||
char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx));
|
||||
|
||||
+2
-1
@@ -44,7 +44,7 @@
|
||||
#define FILE_HEADER_SZ 16
|
||||
|
||||
#ifndef CIPHER_VERSION
|
||||
#define CIPHER_VERSION "3.0.0"
|
||||
#define CIPHER_VERSION "3.0.1"
|
||||
#endif
|
||||
|
||||
#ifndef CIPHER
|
||||
@@ -213,6 +213,7 @@ int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx)
|
||||
|
||||
const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx);
|
||||
int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
|
||||
int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz);
|
||||
#endif
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
#include <Security/SecRandom.h>
|
||||
|
||||
static int sqlcipher_cc_add_random(void *ctx, void *buffer, int length) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* generate a defined number of random bytes */
|
||||
static int sqlcipher_cc_random (void *ctx, void *buffer, int length) {
|
||||
return (SecRandomCopyBytes(kSecRandomDefault, length, (uint8_t *)buffer) == 0) ? SQLITE_OK : SQLITE_ERROR;
|
||||
@@ -132,6 +136,7 @@ int sqlcipher_cc_setup(sqlcipher_provider *p) {
|
||||
p->ctx_cmp = sqlcipher_cc_ctx_cmp;
|
||||
p->ctx_init = sqlcipher_cc_ctx_init;
|
||||
p->ctx_free = sqlcipher_cc_ctx_free;
|
||||
p->add_random = sqlcipher_cc_add_random;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+33
-6
@@ -83,6 +83,7 @@ struct codec_ctx {
|
||||
cipher_ctx *read_ctx;
|
||||
cipher_ctx *write_ctx;
|
||||
unsigned int skip_read_hmac;
|
||||
unsigned int need_kdf_salt;
|
||||
};
|
||||
|
||||
int sqlcipher_register_provider(sqlcipher_provider *p) {
|
||||
@@ -650,8 +651,7 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f
|
||||
if((rc = sqlcipher_cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc;
|
||||
|
||||
if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, FILE_HEADER_SZ, 0) != SQLITE_OK) {
|
||||
/* if unable to read the bytes, generate random salt */
|
||||
if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
|
||||
ctx->need_kdf_salt = 1;
|
||||
}
|
||||
|
||||
if((rc = sqlcipher_codec_ctx_set_cipher(ctx, CIPHER, 0)) != SQLITE_OK) return rc;
|
||||
@@ -823,8 +823,13 @@ static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
|
||||
c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter,
|
||||
ctx->hmac_kdf_salt, c_ctx->fast_kdf_iter, c_ctx->key_sz));
|
||||
|
||||
|
||||
|
||||
if(c_ctx->pass && c_ctx->pass_sz) { // if pass is not null
|
||||
|
||||
if(ctx->need_kdf_salt) {
|
||||
if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
|
||||
ctx->need_kdf_salt = 0;
|
||||
}
|
||||
if (c_ctx->pass_sz == ((c_ctx->key_sz * 2) + 3) && sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0) {
|
||||
int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */
|
||||
const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
|
||||
@@ -990,14 +995,14 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
char *attach_command = sqlite3_mprintf("ATTACH DATABASE '%s-migrated' as migrate KEY '%q';",
|
||||
db_filename, key);
|
||||
|
||||
int rc = sqlcipher_check_connection(db_filename, key, key_sz, "", &user_version);
|
||||
int rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, "", &user_version);
|
||||
if(rc == SQLITE_OK){
|
||||
CODEC_TRACE(("No upgrade required - exiting\n"));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Version 2 - check for 4k with hmac format
|
||||
rc = sqlcipher_check_connection(db_filename, key, key_sz, pragma_4k_kdf_iter, &user_version);
|
||||
rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_4k_kdf_iter, &user_version);
|
||||
if(rc == SQLITE_OK) {
|
||||
CODEC_TRACE(("Version 2 format found\n"));
|
||||
upgrade_4k_format = 1;
|
||||
@@ -1006,7 +1011,7 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
// Version 1 - check both no hmac and 4k together
|
||||
pragma_1x_and_4k = sqlite3_mprintf("%s%s", pragma_hmac_off,
|
||||
pragma_4k_kdf_iter);
|
||||
rc = sqlcipher_check_connection(db_filename, key, key_sz, pragma_1x_and_4k, &user_version);
|
||||
rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_1x_and_4k, &user_version);
|
||||
sqlite3_free(pragma_1x_and_4k);
|
||||
if(rc == SQLITE_OK) {
|
||||
CODEC_TRACE(("Version 1 format found\n"));
|
||||
@@ -1119,6 +1124,28 @@ int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlcipher_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz){
|
||||
const char *suffix = &zRight[random_sz-1];
|
||||
int n = random_sz - 3; /* adjust for leading x' and tailing ' */
|
||||
if (n > 0 &&
|
||||
sqlite3StrNICmp((const char *)zRight ,"x'", 2) == 0 &&
|
||||
sqlite3StrNICmp(suffix, "'", 1) == 0 &&
|
||||
n % 2 == 0) {
|
||||
int rc = 0;
|
||||
int buffer_sz = n / 2;
|
||||
unsigned char *random;
|
||||
const unsigned char *z = (const unsigned char *)zRight + 2; /* adjust lead offset of x' */
|
||||
CODEC_TRACE(("sqlcipher_codec_add_random: using raw random blob from hex\n"));
|
||||
random = sqlcipher_malloc(buffer_sz);
|
||||
memset(random, 0, buffer_sz);
|
||||
cipher_hex2bin(z, n, random);
|
||||
rc = ctx->read_ctx->provider->add_random(ctx->read_ctx->provider_ctx, random, buffer_sz);
|
||||
sqlcipher_free(random, buffer_sz);
|
||||
return rc;
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
/* END SQLCIPHER */
|
||||
|
||||
+72
-39
@@ -35,50 +35,88 @@
|
||||
#include "sqlcipher.h"
|
||||
#include <tomcrypt.h>
|
||||
|
||||
typedef struct {
|
||||
prng_state prng;
|
||||
} ltc_ctx;
|
||||
|
||||
#define FORTUNA_MAX_SZ 32
|
||||
static prng_state prng;
|
||||
static unsigned int ltc_init = 0;
|
||||
static unsigned int ltc_ref_count = 0;
|
||||
static sqlite3_mutex* ltc_rand_mutex = NULL;
|
||||
|
||||
static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
|
||||
ltc_ctx *ltc = (ltc_ctx*)ctx;
|
||||
int rc = fortuna_add_entropy(buffer, length, &(ltc->prng));
|
||||
return rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
|
||||
int rc = 0;
|
||||
int data_to_read = length;
|
||||
int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
const unsigned char * data = (const unsigned char *)buffer;
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
while(data_to_read > 0){
|
||||
rc = fortuna_add_entropy(data, block_sz, &prng);
|
||||
rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
|
||||
if(rc != SQLITE_OK){
|
||||
break;
|
||||
}
|
||||
data_to_read -= block_sz;
|
||||
data += block_sz;
|
||||
block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
|
||||
}
|
||||
fortuna_ready(&prng);
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_activate(void *ctx) {
|
||||
ltc_ctx *ltc = (ltc_ctx*)ctx;
|
||||
int random_buffer_sz = sizeof(char) * 32;
|
||||
unsigned char *random_buffer = sqlcipher_malloc(random_buffer_sz);
|
||||
sqlcipher_memset(random_buffer, 0, random_buffer_sz);
|
||||
|
||||
unsigned char random_buffer[FORTUNA_MAX_SZ];
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
if(ltc_rand_mutex == NULL){
|
||||
ltc_rand_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
||||
}
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
if(ltc_init == 0) {
|
||||
if(register_prng(&fortuna_desc) != CRYPT_OK) return SQLITE_ERROR;
|
||||
if(register_cipher(&rijndael_desc) != CRYPT_OK) return SQLITE_ERROR;
|
||||
if(register_hash(&sha1_desc) != CRYPT_OK) return SQLITE_ERROR;
|
||||
if(fortuna_start(&prng) != CRYPT_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
ltc_init = 1;
|
||||
}
|
||||
if(fortuna_start(&(ltc->prng)) != CRYPT_OK) {
|
||||
ltc_ref_count++;
|
||||
#ifndef SQLCIPHER_TEST
|
||||
sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer);
|
||||
#endif
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqlite3_randomness(random_buffer_sz, random_buffer);
|
||||
if(sqlcipher_ltc_add_random(ctx, random_buffer, random_buffer_sz) != SQLITE_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if(sqlcipher_ltc_add_random(ctx, <c, sizeof(ltc_ctx*)) != SQLITE_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if(fortuna_ready(&(ltc->prng)) != CRYPT_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqlcipher_free(random_buffer, random_buffer_sz);
|
||||
sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_deactivate(void *ctx) {
|
||||
ltc_ctx *ltc = (ltc_ctx*)ctx;
|
||||
fortuna_done(&(ltc->prng));
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
ltc_ref_count--;
|
||||
if(ltc_ref_count == 0){
|
||||
fortuna_done(&prng);
|
||||
sqlcipher_memset((void *)&prng, 0, sizeof(prng));
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
sqlite3_mutex_free(ltc_rand_mutex);
|
||||
ltc_rand_mutex = NULL;
|
||||
#endif
|
||||
}
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
else {
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
}
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -87,13 +125,13 @@ static const char* sqlcipher_ltc_get_provider_name(void *ctx) {
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) {
|
||||
ltc_ctx *ltc = (ltc_ctx*)ctx;
|
||||
int rc;
|
||||
|
||||
if((rc = fortuna_ready(&(ltc->prng))) != CRYPT_OK) {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
fortuna_read(buffer, length, &(ltc->prng));
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_enter(ltc_rand_mutex);
|
||||
#endif
|
||||
fortuna_read(buffer, length, &prng);
|
||||
#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
|
||||
sqlite3_mutex_leave(ltc_rand_mutex);
|
||||
#endif
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -112,7 +150,6 @@ static int sqlcipher_ltc_hmac(void *ctx, unsigned char *hmac_key, int key_sz, un
|
||||
|
||||
static int sqlcipher_ltc_kdf(void *ctx, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
|
||||
int rc, hash_idx;
|
||||
ltc_ctx *ltc = (ltc_ctx*)ctx;
|
||||
unsigned long outlen = key_sz;
|
||||
unsigned long random_buffer_sz = sizeof(char) * 256;
|
||||
unsigned char *random_buffer = sqlcipher_malloc(random_buffer_sz);
|
||||
@@ -173,7 +210,6 @@ static int sqlcipher_ltc_get_hmac_sz(void *ctx) {
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_copy(void *target_ctx, void *source_ctx) {
|
||||
memcpy(target_ctx, source_ctx, sizeof(ltc_ctx));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -182,15 +218,12 @@ static int sqlcipher_ltc_ctx_cmp(void *c1, void *c2) {
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_init(void **ctx) {
|
||||
*ctx = sqlcipher_malloc(sizeof(ltc_ctx));
|
||||
if(*ctx == NULL) return SQLITE_NOMEM;
|
||||
sqlcipher_ltc_activate(*ctx);
|
||||
sqlcipher_ltc_activate(NULL);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int sqlcipher_ltc_ctx_free(void **ctx) {
|
||||
sqlcipher_ltc_deactivate(&ctx);
|
||||
sqlcipher_free(*ctx, sizeof(ltc_ctx));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
+102
-3
@@ -87,6 +87,21 @@ proc if_built_with_commoncrypto {name cmd expected} {
|
||||
}
|
||||
}
|
||||
|
||||
proc cmpFilesChunked {file1 file2 {chunksize 16384}} {
|
||||
set f1 [open $file1]; fconfigure $f1 -translation binary
|
||||
set f2 [open $file2]; fconfigure $f2 -translation binary
|
||||
while {1} {
|
||||
set d1 [read $f1 $chunksize]
|
||||
set d2 [read $f2 $chunksize]
|
||||
set diff [string compare $d1 $d2]
|
||||
if {$diff != 0 || [eof $f1] || [eof $f2]} {
|
||||
close $f1; close $f2
|
||||
return $diff
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
# The database is initially empty.
|
||||
# set an hex key create some basic data
|
||||
# create table and insert operations should work
|
||||
@@ -413,9 +428,9 @@ file delete -force test.db
|
||||
setup test.db "'testkey'"
|
||||
do_test attach-database-with-default-key {
|
||||
sqlite_orig db2 test2.db
|
||||
|
||||
execsql {
|
||||
PRAGMA key = 'testkey';
|
||||
PRAGMA cipher_add_random = "x'deadbaad'";
|
||||
CREATE TABLE t2(a,b);
|
||||
INSERT INTO t2 VALUES ('test1', 'test2');
|
||||
} db2
|
||||
@@ -423,7 +438,7 @@ do_test attach-database-with-default-key {
|
||||
catchsql {
|
||||
ATTACH 'test.db' AS db;
|
||||
} db2
|
||||
|
||||
|
||||
} {1 {file is encrypted or is not a database}}
|
||||
db2 close
|
||||
file delete -force test.db
|
||||
@@ -1483,7 +1498,7 @@ do_test verify-pragma-cipher-version {
|
||||
execsql {
|
||||
PRAGMA cipher_version;
|
||||
}
|
||||
} {3.0.0}
|
||||
} {3.0.1}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
@@ -2114,5 +2129,89 @@ db close
|
||||
file delete -force test.db
|
||||
file delete -force new.db
|
||||
|
||||
# Requires SQLCipher to be built with -DSQLCIPHER_TEST
|
||||
if_built_with_libtomcrypt verify-random-data-alters-file-content {
|
||||
file delete -force test.db
|
||||
file delete -force test2.db
|
||||
file delete -force test3.db
|
||||
set rc {}
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
create table t1(a,b);
|
||||
}
|
||||
db close
|
||||
sqlite_orig db test2.db
|
||||
execsql {
|
||||
PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
create table t1(a,b);
|
||||
}
|
||||
db close
|
||||
sqlite_orig db test3.db
|
||||
execsql {
|
||||
PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA cipher_add_random = "x'deadbaad'";
|
||||
create table t1(a,b);
|
||||
}
|
||||
db close
|
||||
lappend rc [cmpFilesChunked test.db test2.db]
|
||||
lappend rc [cmpFilesChunked test2.db test3.db]
|
||||
} {0 1}
|
||||
file delete -force test.db
|
||||
file delete -force test2.db
|
||||
file delete -force test3.db
|
||||
|
||||
do_test can-migrate-with-keys-longer-than-64-characters {
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
|
||||
PRAGMA kdf_iter = 4000;
|
||||
PRAGMA user_version = 5;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
|
||||
PRAGMA user_version;
|
||||
}
|
||||
} {5}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
do_test can-migrate-with-raw-hex-key {
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA kdf_iter = 4000;
|
||||
PRAGMA cipher_use_hmac = off;
|
||||
PRAGMA user_version = 5;
|
||||
}
|
||||
db close
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA cipher_migrate;
|
||||
}
|
||||
|
||||
sqlite_orig db test.db
|
||||
execsql {
|
||||
PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
|
||||
PRAGMA user_version;
|
||||
}
|
||||
|
||||
} {5}
|
||||
db close
|
||||
file delete -force test.db
|
||||
|
||||
sqlite3_test_control_pending_byte $old_pending_byte
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user