From f1342ec17757103f07c8f12cc958baeb1883e2e0 Mon Sep 17 00:00:00 2001 From: Felipe Gasper Date: Wed, 31 Mar 2021 20:33:57 -0400 Subject: [PATCH] Give upgraded strings to SQLite as downgraded when sqlite_unicode==0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #66: If $a and $b contain the same code points but one is upgraded and the other isn’t, previously DBD::SQLite would give two different C strings to SQLite. This changeset fixes that so that SQLite always translates equivalent Perl strings to the same C string, regardless of the Perl strings’ internal encoding. --- dbdimp.c | 12 ++++++-- t/68_upgraded_no_unicode.t | 61 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 t/68_upgraded_no_unicode.t diff --git a/dbdimp.c b/dbdimp.c index d30e98e..2452821 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -549,6 +549,9 @@ sqlite_db_do_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *sv_statement) if (imp_dbh->unicode) { sv_utf8_upgrade(sv_statement); } + else { + sv_utf8_downgrade(sv_statement, 0); + } statement = SvPV_nolen(sv_statement); @@ -893,6 +896,9 @@ sqlite_st_prepare_sv(SV *sth, imp_sth_t *imp_sth, SV *sv_statement, SV *attribs) if (imp_dbh->unicode) { sv_utf8_upgrade(sv_statement); } + else { + sv_utf8_downgrade(sv_statement, 0); + } statement = SvPV_nolen(sv_statement); @@ -1007,9 +1013,11 @@ sqlite_st_execute(SV *sth, imp_sth_t *imp_sth) int numtype = 0; if (imp_dbh->unicode) { - sv_utf8_upgrade(value); + data = SvPVutf8(value, len); + } + else { + data = SvPVbyte(value, len); } - data = SvPV(value, len); /* * XXX: For backward compatibility, it'd be better to diff --git a/t/68_upgraded_no_unicode.t b/t/68_upgraded_no_unicode.t new file mode 100644 index 0000000..34d745f --- /dev/null +++ b/t/68_upgraded_no_unicode.t @@ -0,0 +1,61 @@ +# This is a test for correct handling of upgraded strings without +# the sqlite_unicode parameter. + +use strict; +use warnings; +use lib "t/lib"; +use SQLiteTest; +use Test::More; +use if -d ".git", "Test::FailWarnings"; + +{ + my $dbh = connect_ok( dbfile => 'foo', RaiseError => 1 ); + + my $tbl_name = "\xe9p\xe9e"; + utf8::encode $tbl_name; + + my $str = "CREATE TABLE $tbl_name ( col1 TEXT )"; + utf8::upgrade $str; + + $dbh->do($str); + + my $master_ar = $dbh->selectall_arrayref('SELECT * FROM sqlite_master', { Slice => {} }); + + is( + $master_ar->[0]{'name'}, + $tbl_name, + 'do() takes correct string value', + ); + + #---------------------------------------------------------------------- + + my $dummy_str = "SELECT '$tbl_name'"; + utf8::upgrade $dummy_str; + + my $sth = $dbh->prepare($dummy_str); + $sth->execute(); + my $row = $sth->fetchrow_arrayref(); + + is( + $row->[0], + $tbl_name, + 'prepare() takes correct string value', + ); + + #---------------------------------------------------------------------- + + my $tbl_name_ug = $tbl_name; + utf8::upgrade $tbl_name_ug; + + my $sth2 = $dbh->prepare('SELECT ?'); + $sth2->execute( do { my $v = $tbl_name_ug } ); + $row = $sth2->fetchrow_arrayref(); + + is( + $row->[0], + $tbl_name, + 'execute() takes correct string value', + ); +} + +done_testing;