]> Sergey Matveev's repositories - public-inbox.git/commitdiff
sharedkv: avoid ambiguity for numeric-like string keys
authorEric Wong <e@80x24.org>
Mon, 14 Feb 2022 05:37:25 +0000 (05:37 +0000)
committerEric Wong <e@80x24.org>
Mon, 14 Feb 2022 18:43:16 +0000 (18:43 +0000)
While we only store URLs and binary SHA-1/SHA-256 values in skv
at the moment, we may store potentially ambiguous keys/values in
the future.  It's possible to store "02" and have it treated as
`2' unless explicitly binding parameters as SQL_BLOB.  This
behavior was independent of the sqlite_unicode parameter as
evidenced by the new tests.

I only noticed this bug while hacking on another project using
DBD::SQLite, and not while hacking on public-inbox itself.

lib/PublicInbox/SharedKV.pm
t/shared_kv.t

index d49a39c1b28ce38d95cb76229a91c1633563c08e..90ccf2b4388c34dc48b21ad647c60b47b1ab98c0 100644 (file)
@@ -9,7 +9,7 @@ use strict;
 use v5.10.1;
 use parent qw(PublicInbox::Lock);
 use File::Temp qw(tempdir);
 use v5.10.1;
 use parent qw(PublicInbox::Lock);
 use File::Temp qw(tempdir);
-use DBI ();
+use DBI qw(:sql_types); # SQL_BLOB
 use PublicInbox::Spawn;
 use File::Path qw(rmtree make_path);
 
 use PublicInbox::Spawn;
 use File::Path qw(rmtree make_path);
 
@@ -59,9 +59,12 @@ sub new {
 sub set_maybe {
        my ($self, $key, $val, $lock) = @_;
        $lock //= $self->lock_for_scope_fast;
 sub set_maybe {
        my ($self, $key, $val, $lock) = @_;
        $lock //= $self->lock_for_scope_fast;
-       my $e = $self->{dbh}->prepare_cached(<<'')->execute($key, $val);
+       my $sth = $self->{dbh}->prepare_cached(<<'');
 INSERT OR IGNORE INTO kv (k,v) VALUES (?, ?)
 
 INSERT OR IGNORE INTO kv (k,v) VALUES (?, ?)
 
+       $sth->bind_param(1, $key, SQL_BLOB);
+       $sth->bind_param(2, $val, SQL_BLOB);
+       my $e = $sth->execute;
        $e == 0 ? undef : $e;
 }
 
        $e == 0 ? undef : $e;
 }
 
@@ -88,20 +91,30 @@ sub keys {
        } else {
                @pfx = (); # [0] may've been undef
        }
        } else {
                @pfx = (); # [0] may've been undef
        }
-       map { $_->[0] } @{$self->dbh->selectall_arrayref($sql, undef, @pfx)};
+       my $sth = $self->dbh->prepare($sql);
+       if (@pfx) {
+               $sth->bind_param(1, $pfx[0], SQL_BLOB);
+               $sth->bind_param(2, $pfx[1]);
+       }
+       $sth->execute;
+       map { $_->[0] } @{$sth->fetchall_arrayref};
 }
 
 sub set {
        my ($self, $key, $val) = @_;
        if (defined $val) {
 }
 
 sub set {
        my ($self, $key, $val) = @_;
        if (defined $val) {
-               my $e = $self->{dbh}->prepare_cached(<<'')->execute($key, $val);
+               my $sth = $self->{dbh}->prepare_cached(<<'');
 INSERT OR REPLACE INTO kv (k,v) VALUES (?,?)
 
 INSERT OR REPLACE INTO kv (k,v) VALUES (?,?)
 
+               $sth->bind_param(1, $key, SQL_BLOB);
+               $sth->bind_param(2, $val, SQL_BLOB);
+               my $e = $sth->execute;
                $e == 0 ? undef : $e;
        } else {
                $e == 0 ? undef : $e;
        } else {
-               $self->{dbh}->prepare_cached(<<'')->execute($key);
+               my $sth = $self->{dbh}->prepare_cached(<<'');
 DELETE FROM kv WHERE k = ?
 
 DELETE FROM kv WHERE k = ?
 
+               $sth->bind_param(1, $key, SQL_BLOB);
        }
 }
 
        }
 }
 
@@ -110,7 +123,8 @@ sub get {
        my $sth = $self->{dbh}->prepare_cached(<<'', undef, 1);
 SELECT v FROM kv WHERE k = ?
 
        my $sth = $self->{dbh}->prepare_cached(<<'', undef, 1);
 SELECT v FROM kv WHERE k = ?
 
-       $sth->execute($key);
+       $sth->bind_param(1, $key, SQL_BLOB);
+       $sth->execute;
        $sth->fetchrow_array;
 }
 
        $sth->fetchrow_array;
 }
 
@@ -121,9 +135,11 @@ sub xchg {
        if (defined $newval) {
                set($self, $key, $newval);
        } else {
        if (defined $newval) {
                set($self, $key, $newval);
        } else {
-               $self->{dbh}->prepare_cached(<<'')->execute($key);
+               my $sth = $self->{dbh}->prepare_cached(<<'');
 DELETE FROM kv WHERE k = ?
 
 DELETE FROM kv WHERE k = ?
 
+               $sth->bind_param(1, $key, SQL_BLOB);
+               $sth->execute;
        }
        $oldval;
 }
        }
        $oldval;
 }
index 8b4f9c29ea9553971208a52ccf67b27a8e931b8d..8dfd3b2585780299a0ad15d3a854ac3995de9005 100644 (file)
@@ -42,5 +42,8 @@ undef $skv;
 ok(!-d $skv_tmpdir, 'temporary dir gone');
 $skv = PublicInbox::SharedKV->new("$tmpdir/dir", 'base');
 ok(-e "$tmpdir/dir/base.sqlite3", 'file created');
 ok(!-d $skv_tmpdir, 'temporary dir gone');
 $skv = PublicInbox::SharedKV->new("$tmpdir/dir", 'base');
 ok(-e "$tmpdir/dir/base.sqlite3", 'file created');
+$skv->dbh;
+ok($skv->set_maybe('02', '2'), "`02' set");
+ok($skv->set_maybe('2', '2'), "`2' set (no match on `02')");
 
 done_testing;
 
 done_testing;