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/62_regexp_multibyte_char_class.t
|
||||||
t/63_param_values.t
|
t/63_param_values.t
|
||||||
t/64_limit.t
|
t/64_limit.t
|
||||||
|
t/65_db_config.t
|
||||||
t/cookbook_variance.t
|
t/cookbook_variance.t
|
||||||
t/lib/SQLiteTest.pm
|
t/lib/SQLiteTest.pm
|
||||||
t/rt_106151_outermost_savepoint.t
|
t/rt_106151_outermost_savepoint.t
|
||||||
|
|
14
SQLite.xs
14
SQLite.xs
|
@ -354,6 +354,20 @@ limit(dbh, id, new_value = -1)
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
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
|
MODULE = DBD::SQLite PACKAGE = DBD::SQLite::st
|
||||||
|
|
||||||
PROTOTYPES: DISABLE
|
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);
|
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_tokenizer.inc"
|
||||||
#include "dbdimp_virtual_table.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);
|
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_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_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);
|
int sqlite_db_do_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *sv_statement);
|
||||||
void init_cxt();
|
void init_cxt();
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ sub driver {
|
||||||
DBD::SQLite::st->install_method('sqlite_st_status', { O => 0x0004 });
|
DBD::SQLite::st->install_method('sqlite_st_status', { O => 0x0004 });
|
||||||
DBD::SQLite::db->install_method('sqlite_create_module');
|
DBD::SQLite::db->install_method('sqlite_create_module');
|
||||||
DBD::SQLite::db->install_method('sqlite_limit');
|
DBD::SQLite::db->install_method('sqlite_limit');
|
||||||
|
DBD::SQLite::db->install_method('sqlite_db_config');
|
||||||
|
|
||||||
$methods_are_installed++;
|
$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.
|
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()
|
=head2 $dbh->sqlite_create_module()
|
||||||
|
|
||||||
Registers a name for a I<virtual table module>. Module names must be
|
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