mirror of
https://github.com/DBD-SQLite/DBD-SQLite
synced 2025-06-07 14:19:10 -04:00
implemented sqlite_db_config interface (RT-128056)
This commit is contained in:
parent
b8c5535548
commit
a28532af37
6 changed files with 251 additions and 0 deletions
1
MANIFEST
1
MANIFEST
|
@ -90,6 +90,7 @@ t/61_strlike.t
|
|||
t/62_regexp_multibyte_char_class.t
|
||||
t/63_param_values.t
|
||||
t/64_limit.t
|
||||
t/65_db_config.t
|
||||
t/cookbook_variance.t
|
||||
t/lib/SQLiteTest.pm
|
||||
t/rt_106151_outermost_savepoint.t
|
||||
|
|
14
SQLite.xs
14
SQLite.xs
|
@ -354,6 +354,20 @@ limit(dbh, id, new_value = -1)
|
|||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
static int
|
||||
db_config(dbh, id, new_value = -1)
|
||||
SV *dbh
|
||||
int id
|
||||
int new_value
|
||||
ALIAS:
|
||||
DBD::SQLite::db::sqlite_db_config = 1
|
||||
CODE:
|
||||
{
|
||||
RETVAL = sqlite_db_config(aTHX_ dbh, id, new_value);
|
||||
}
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
MODULE = DBD::SQLite PACKAGE = DBD::SQLite::st
|
||||
|
||||
PROTOTYPES: DISABLE
|
||||
|
|
35
dbdimp.c
35
dbdimp.c
|
@ -2756,6 +2756,41 @@ sqlite_db_limit(pTHX_ SV *dbh, int id, int new_value)
|
|||
return sqlite3_limit(imp_dbh->db, id, new_value);
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_db_config(pTHX_ SV *dbh, int id, int new_value)
|
||||
{
|
||||
D_imp_dbh(dbh);
|
||||
int ret;
|
||||
int rc = -1;
|
||||
switch (id) {
|
||||
case SQLITE_DBCONFIG_LOOKASIDE:
|
||||
sqlite_error(dbh, rc, "SQLITE_DBCONFIG_LOOKASIDE is not supported");
|
||||
return FALSE;
|
||||
case SQLITE_DBCONFIG_MAINDBNAME:
|
||||
sqlite_error(dbh, rc, "SQLITE_DBCONFIG_MAINDBNAME is not supported");
|
||||
return FALSE;
|
||||
case SQLITE_DBCONFIG_ENABLE_FKEY:
|
||||
case SQLITE_DBCONFIG_ENABLE_TRIGGER:
|
||||
case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
|
||||
case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
|
||||
case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
|
||||
case SQLITE_DBCONFIG_ENABLE_QPSG:
|
||||
case SQLITE_DBCONFIG_TRIGGER_EQP:
|
||||
case SQLITE_DBCONFIG_RESET_DATABASE:
|
||||
case SQLITE_DBCONFIG_DEFENSIVE:
|
||||
rc = sqlite3_db_config(imp_dbh->db, id, new_value, &ret);
|
||||
break;
|
||||
default:
|
||||
sqlite_error(dbh, rc, form("Unknown config id: %d", id));
|
||||
return FALSE;
|
||||
}
|
||||
if ( rc != SQLITE_OK ) {
|
||||
sqlite_error(dbh, rc, form("sqlite_config failed with error %s", sqlite3_errmsg(imp_dbh->db)));
|
||||
return FALSE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include "dbdimp_tokenizer.inc"
|
||||
#include "dbdimp_virtual_table.inc"
|
||||
|
||||
|
|
1
dbdimp.h
1
dbdimp.h
|
@ -135,6 +135,7 @@ HV* _sqlite_status(int reset);
|
|||
HV* _sqlite_st_status(pTHX_ SV *sth, int reset);
|
||||
int sqlite_db_create_module(pTHX_ SV *dbh, const char *name, const char *perl_class);
|
||||
int sqlite_db_limit(pTHX_ SV *dbh, int id, int new_value);
|
||||
int sqlite_db_config(pTHX_ SV *dbh, int id, int new_value);
|
||||
int sqlite_db_do_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *sv_statement);
|
||||
void init_cxt();
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ sub driver {
|
|||
DBD::SQLite::st->install_method('sqlite_st_status', { O => 0x0004 });
|
||||
DBD::SQLite::db->install_method('sqlite_create_module');
|
||||
DBD::SQLite::db->install_method('sqlite_limit');
|
||||
DBD::SQLite::db->install_method('sqlite_db_config');
|
||||
|
||||
$methods_are_installed++;
|
||||
}
|
||||
|
@ -2304,6 +2305,20 @@ Returns a hash reference that holds a set of status information of SQLite statem
|
|||
|
||||
You may also pass 0 as an argument to reset the status.
|
||||
|
||||
=head2 $dbh->sqlite_db_config( $id, $new_integer_value )
|
||||
|
||||
You can change how the connected database should behave like this:
|
||||
|
||||
use DBD::SQLite::Constants qw/:database_connection_configuration_options/;
|
||||
|
||||
my $dbh = DBI->connect('dbi:SQLite::memory:');
|
||||
|
||||
# This disables language features that allow ordinary SQL
|
||||
# to deliberately corrupt the database file
|
||||
$dbh->sqlite_db_config( SQLITE_DBCONFIG_DEFENSIVE, 1 );
|
||||
|
||||
As of this writing, C<sqlite_db_config> only supports options that set an integer value. C<SQLITE_DBCONFIG_LOOKASIDE> and C<SQLITE_DBCONFIG_MAINDBNAME> are not supported. See also C<https://www.sqlite.org/capi3ref.html#sqlite3_db_config> for details.
|
||||
|
||||
=head2 $dbh->sqlite_create_module()
|
||||
|
||||
Registers a name for a I<virtual table module>. Module names must be
|
||||
|
|
185
t/65_db_config.t
Normal file
185
t/65_db_config.t
Normal file
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
BEGIN {
|
||||
$| = 1;
|
||||
$^W = 1;
|
||||
}
|
||||
|
||||
use lib "t/lib";
|
||||
use SQLiteTest qw/connect_ok @CALL_FUNCS/;
|
||||
use Test::More;
|
||||
use DBD::SQLite::Constants qw/:database_connection_configuration_options/;
|
||||
|
||||
#plan tests => 7 * @CALL_FUNCS;
|
||||
|
||||
# LOOKASIDE
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'LOOKASIDE is not supported', 2 if !SQLITE_DBCONFIG_LOOKASIDE;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
eval { $dbh->$func(SQLITE_DBCONFIG_LOOKASIDE, 1, 'db_config') };
|
||||
ok $@, 'LOOKASIDE is not supported';
|
||||
like $@ => qr/LOOKASIDE is not supported/;
|
||||
}
|
||||
}
|
||||
|
||||
# MAINDBNAME
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'MAINDBNAME is not supported', 2 if !SQLITE_DBCONFIG_MAINDBNAME;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
eval { $dbh->$func(SQLITE_DBCONFIG_MAINDBNAME, 1, 'db_config') };
|
||||
ok $@, 'MAINDBNAME is not supported';
|
||||
like $@ => qr/MAINDBNAME is not supported/;
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_FKEY
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'ENABLE_FKEY is not supported', 3 if !SQLITE_DBCONFIG_ENABLE_FKEY;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_FKEY, 1, 'db_config');
|
||||
is $ret => 1, 'enable foreign key';
|
||||
|
||||
# TODO: add foreign key check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_FKEY, 0, 'db_config');
|
||||
is $ret => 0, 'disable foreign key';
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_TRIGGER
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'ENABLE_TRIGGER is not supported', 3 if !SQLITE_DBCONFIG_ENABLE_TRIGGER;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_TRIGGER, 1, 'db_config');
|
||||
is $ret => 1, 'enable trigger';
|
||||
|
||||
# TODO: add trigger check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, 'db_config');
|
||||
is $ret => 0, 'disable trigger';
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_FTS3_TOKENIZER
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'ENABLE_FTS3_TOKENIZER is not supported', 3 if !SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 'db_config');
|
||||
is $ret => 1, 'enable fts3_tokenizer';
|
||||
|
||||
# TODO: add fts3_tokenizer check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 0, 'db_config');
|
||||
is $ret => 0, 'disable fts3_tokenizer';
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_LOAD_EXTENSION
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'ENABLE_LOAD_EXTENSION is not supported', 3 if !SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 'db_config');
|
||||
is $ret => 1, 'enable load_extension';
|
||||
|
||||
# TODO: add load_extension check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, 'db_config');
|
||||
is $ret => 0, 'disable load_extension';
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_NO_CKPT_ON_CLOSE
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'NO_CKPT_ON_CLOSE is not supported', 3 if !SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 1, 'db_config');
|
||||
is $ret => 1, 'no checkpoint on close';
|
||||
|
||||
# TODO: add checkpoint check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 0, 'db_config');
|
||||
is $ret => 0, 'checkpoint on close';
|
||||
}
|
||||
}
|
||||
|
||||
# ENABLE_QPSG
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'ENABLE_QPSG is not supported', 3 if !SQLITE_DBCONFIG_ENABLE_QPSG;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_QPSG, 1, 'db_config');
|
||||
is $ret => 1, 'enable query planner stability guarantee';
|
||||
|
||||
# TODO: add qpsg check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_ENABLE_QPSG, 0, 'db_config');
|
||||
is $ret => 0, 'disable query planner stability guarantee';
|
||||
}
|
||||
}
|
||||
|
||||
# TRIGGER_EQP
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'TRIGGER_EQP is not supported', 3 if !SQLITE_DBCONFIG_TRIGGER_EQP;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_TRIGGER_EQP, 1, 'db_config');
|
||||
is $ret => 1, 'trigger explain query plan';
|
||||
|
||||
# TODO: add trigger check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_TRIGGER_EQP, 0, 'db_config');
|
||||
is $ret => 0, 'no trigger explain query plan';
|
||||
}
|
||||
}
|
||||
|
||||
# RESET_DATABASE
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'RESET_DATABASE is not supported', 3 if !SQLITE_DBCONFIG_RESET_DATABASE;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_RESET_DATABASE, 1, 'db_config');
|
||||
is $ret => 1, 'enable reset database';
|
||||
|
||||
# TODO: add reset check
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_RESET_DATABASE, 0, 'db_config');
|
||||
is $ret => 0, 'disable reset database';
|
||||
}
|
||||
}
|
||||
|
||||
# DEFENSIVE
|
||||
for my $func (@CALL_FUNCS) {
|
||||
SKIP: {
|
||||
skip 'DEFENSIVE is not supported', 8 if !SQLITE_DBCONFIG_DEFENSIVE;
|
||||
my $dbh = connect_ok(RaiseError => 1, PrintError => 0);
|
||||
|
||||
my $sql = 'CREATE TABLE foo (id, text)';
|
||||
$dbh->do($sql);
|
||||
$dbh->do('PRAGMA writable_schema=ON');
|
||||
my $row = $dbh->selectrow_hashref('SELECT * FROM sqlite_master WHERE name = ?', {Slice => +{}}, 'foo');
|
||||
is $row->{sql} => $sql, 'found sql';
|
||||
|
||||
my $ret = $dbh->$func(SQLITE_DBCONFIG_DEFENSIVE, 1, 'db_config');
|
||||
is $ret => 1;
|
||||
eval { $dbh->do('UPDATE sqlite_master SET name = ? WHERE name = ?', undef, 'bar', 'foo'); };
|
||||
ok $@, "updating sqlite_master is prohibited";
|
||||
like $@ => qr/table sqlite_master may not be modified/;
|
||||
|
||||
$ret = $dbh->$func(SQLITE_DBCONFIG_DEFENSIVE, 0, 'db_config');
|
||||
is $ret => 0;
|
||||
$ret = $dbh->do('UPDATE sqlite_master SET name = ? WHERE name = ?', undef, 'bar', 'foo');
|
||||
ok $ret, 'updating sqlite_master is succeeded';
|
||||
$row = $dbh->selectrow_hashref('SELECT * FROM sqlite_master WHERE name = ?', {Slice => +{}}, 'foo');
|
||||
ok !$row, 'sql not found';
|
||||
}
|
||||
}
|
||||
|
||||
done_testing;
|
Loading…
Add table
Reference in a new issue