From fd3c8b0a63d01fad367e699c50b21b3c02d45852 Mon Sep 17 00:00:00 2001 From: Keita Jamadam Sugama Date: Mon, 8 Sep 2025 15:17:33 +0900 Subject: [PATCH 1/6] quote aliases in select list --- lib/Data/ObjectDriver/SQL.pm | 2 +- t/11-sql.t | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Data/ObjectDriver/SQL.pm b/lib/Data/ObjectDriver/SQL.pm index 8c43856..c4ea90e 100644 --- a/lib/Data/ObjectDriver/SQL.pm +++ b/lib/Data/ObjectDriver/SQL.pm @@ -65,7 +65,7 @@ sub as_sql { $sql .= 'DISTINCT ' if $stmt->distinct; $sql .= join(', ', map { my $alias = $stmt->select_map->{$_}; - $alias && /(?:^|\.)\Q$alias\E$/ ? $_ : "$_ $alias"; + $alias && /(?:^|\.)\Q$alias\E$/ ? $_ : qq!$_ "$alias"!; } @{ $stmt->select }) . "\n"; } $sql .= 'FROM '; diff --git a/t/11-sql.t b/t/11-sql.t index 56a21ad..621e6de 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -288,7 +288,7 @@ $stmt = ns(); $stmt->add_select('f.foo' => 'foo'); $stmt->add_select('COUNT(*)' => 'count'); $stmt->from([ qw( baz ) ]); -is($stmt->as_sql, "SELECT f.foo, COUNT(*) count\nFROM baz\n"); +is($stmt->as_sql, qq!SELECT f.foo, COUNT(*) "count"\nFROM baz\n!); my $map = $stmt->select_map; is(scalar(keys %$map), 2); is($map->{'f.foo'}, 'foo'); @@ -306,7 +306,7 @@ $stmt->limit(2); $stmt->add_having(count => 2); is($stmt->as_sql, < Date: Thu, 11 Sep 2025 03:30:18 +0900 Subject: [PATCH 2/6] use quote_identifer method if possible --- lib/Data/ObjectDriver/Driver/DBI.pm | 2 +- lib/Data/ObjectDriver/SQL.pm | 10 +++++-- t/11-sql.t | 41 ++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/Data/ObjectDriver/Driver/DBI.pm b/lib/Data/ObjectDriver/Driver/DBI.pm index 148db0d..7df6851 100644 --- a/lib/Data/ObjectDriver/Driver/DBI.pm +++ b/lib/Data/ObjectDriver/Driver/DBI.pm @@ -711,7 +711,7 @@ sub prepare_statement { my($class, $terms, $args) = @_; my $dbd = $driver->dbd; - my $stmt = $args->{sql_statement} || $dbd->sql_class->new; + my $stmt = $args->{sql_statement} || $dbd->sql_class->new({dbh => $driver->rw_handle}); if (my $tbl = $driver->table_for($class)) { my $cols = $class->column_names; diff --git a/lib/Data/ObjectDriver/SQL.pm b/lib/Data/ObjectDriver/SQL.pm index c4ea90e..41ebce2 100644 --- a/lib/Data/ObjectDriver/SQL.pm +++ b/lib/Data/ObjectDriver/SQL.pm @@ -10,7 +10,7 @@ __PACKAGE__->mk_accessors(qw( select distinct select_map select_map_reverse from joins where bind limit offset group order having where_values column_mutator index_hint - comment + comment dbh )); sub new { @@ -65,7 +65,13 @@ sub as_sql { $sql .= 'DISTINCT ' if $stmt->distinct; $sql .= join(', ', map { my $alias = $stmt->select_map->{$_}; - $alias && /(?:^|\.)\Q$alias\E$/ ? $_ : qq!$_ "$alias"!; + if ($alias && !/(?:^|\.)\Q$alias\E$/) { + my $dbh = $stmt->dbh; + $alias = $dbh->quote_identifier($alias) if $dbh; + qq!$_ $alias! + } else { + $_; + } } @{ $stmt->select }) . "\n"; } $sql .= 'FROM '; diff --git a/t/11-sql.t b/t/11-sql.t index 621e6de..277745a 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -2,8 +2,16 @@ use strict; +use lib 't/lib'; use Data::ObjectDriver::SQL; -use Test::More tests => 113; +use Test::More tests => 114; +use DodTestUtil; + +BEGIN { DodTestUtil->check_driver } + +setup_dbs({ + global => [ qw( wines ) ], +}); my $stmt = ns(); ok($stmt, 'Created SQL object'); @@ -288,7 +296,7 @@ $stmt = ns(); $stmt->add_select('f.foo' => 'foo'); $stmt->add_select('COUNT(*)' => 'count'); $stmt->from([ qw( baz ) ]); -is($stmt->as_sql, qq!SELECT f.foo, COUNT(*) "count"\nFROM baz\n!); +is($stmt->as_sql, qq!SELECT f.foo, COUNT(*) count\nFROM baz\n!); my $map = $stmt->select_map; is(scalar(keys %$map), 2); is($map->{'f.foo'}, 'foo'); @@ -306,7 +314,7 @@ $stmt->limit(2); $stmt->add_having(count => 2); is($stmt->as_sql, <new } +subtest 'quote can be used based on given dbh' => sub { + use Wine; + $stmt = ns({dbh => Wine->driver->rw_handle}); + $stmt->add_select(foo => 'bar'); + @{$stmt->from} = ('bar'); + my $quoted = Wine->driver->dbh->quote_identifier('bar'); + is sql_normalize($stmt->as_sql), sql_normalize(<<"EOF"), 'right sql'; +SELECT foo $quoted FROM bar +EOF +}; + +sub ns { Data::ObjectDriver::SQL->new(@_) } + +sub sql_normalize { + my $sql = shift; + $sql =~ s{\s+}{ }g; + $sql =~ s{\( }{(}g; + $sql =~ s{ \)}{)}g; + $sql =~ s{([\(\)]) ([\(\)])}{$1$2}g; + $sql; +} + +END { + disconnect_all(qw/Wine/); + teardown_dbs(qw( global )); +} From 0279971faeff70169734b537714dc25b90a4d645 Mon Sep 17 00:00:00 2001 From: Keita Jamadam Sugama Date: Thu, 11 Sep 2025 09:09:59 +0900 Subject: [PATCH 3/6] fix confusing test --- t/11-sql.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/11-sql.t b/t/11-sql.t index 277745a..317b2b3 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -399,9 +399,9 @@ subtest 'quote can be used based on given dbh' => sub { $stmt = ns({dbh => Wine->driver->rw_handle}); $stmt->add_select(foo => 'bar'); @{$stmt->from} = ('bar'); - my $quoted = Wine->driver->dbh->quote_identifier('bar'); + my $quoted = Wine->driver->dbh->quote_identifier('baz'); is sql_normalize($stmt->as_sql), sql_normalize(<<"EOF"), 'right sql'; -SELECT foo $quoted FROM bar +SELECT foo $quoted FROM baz EOF }; From e512e50eb0dd87dd3dd4047673baace89fe759a3 Mon Sep 17 00:00:00 2001 From: Keita Jamadam Sugama Date: Thu, 11 Sep 2025 09:13:05 +0900 Subject: [PATCH 4/6] oops --- t/11-sql.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/11-sql.t b/t/11-sql.t index 317b2b3..c0744b8 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -398,8 +398,8 @@ subtest 'quote can be used based on given dbh' => sub { use Wine; $stmt = ns({dbh => Wine->driver->rw_handle}); $stmt->add_select(foo => 'bar'); - @{$stmt->from} = ('bar'); - my $quoted = Wine->driver->dbh->quote_identifier('baz'); + @{$stmt->from} = ('baz'); + my $quoted = Wine->driver->dbh->quote_identifier('bar'); is sql_normalize($stmt->as_sql), sql_normalize(<<"EOF"), 'right sql'; SELECT foo $quoted FROM baz EOF From 5dd60a551e2b3236527539b85edf900bce43cc4f Mon Sep 17 00:00:00 2001 From: Keita Jamadam Sugama Date: Thu, 11 Sep 2025 16:07:59 +0900 Subject: [PATCH 5/6] subquery alias should be set to select_map --- t/11-sql.t | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/t/11-sql.t b/t/11-sql.t index c0744b8..644f009 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -4,7 +4,7 @@ use strict; use lib 't/lib'; use Data::ObjectDriver::SQL; -use Test::More tests => 114; +use Test::More tests => 111; use DodTestUtil; BEGIN { DodTestUtil->check_driver } @@ -292,15 +292,34 @@ $stmt->add_select('bar'); $stmt->from([ qw( baz ) ]); is($stmt->as_sql, "SELECT foo, bar\nFROM baz\n"); -$stmt = ns(); -$stmt->add_select('f.foo' => 'foo'); -$stmt->add_select('COUNT(*)' => 'count'); -$stmt->from([ qw( baz ) ]); -is($stmt->as_sql, qq!SELECT f.foo, COUNT(*) count\nFROM baz\n!); -my $map = $stmt->select_map; -is(scalar(keys %$map), 2); -is($map->{'f.foo'}, 'foo'); -is($map->{'COUNT(*)'}, 'count'); +subtest 'SQL functions' => sub { + $stmt = ns(); + $stmt->add_select('f.foo' => 'foo'); + $stmt->add_select('COUNT(*)' => 'count'); + $stmt->from([ qw( baz ) ]); + is($stmt->as_sql, "SELECT f.foo, COUNT(*) count\nFROM baz\n"); + my $map = $stmt->select_map; + is(scalar(keys %$map), 2); + is_deeply($map, {'f.foo' => 'foo', 'COUNT(*)' => 'count'}, 'right map'); + + $stmt = ns(); + $stmt->add_select('count(foo)'); + $stmt->add_select('count(bar)'); + $stmt->from([qw( baz )]); + is($stmt->as_sql, "SELECT count(foo), count(bar)\nFROM baz\n"); + my $map = $stmt->select_map; + is(scalar(keys %$map), 2); + is_deeply($map, {'count(foo)' => 'count(foo)', 'count(bar)' => 'count(bar)'}, 'right map'); + + $stmt = ns(); + $stmt->add_select('count(foo)', 'count1'); + $stmt->add_select('count(bar)', 'count2'); + $stmt->from([qw( baz )]); + is($stmt->as_sql, "SELECT count(foo) count1, count(bar) count2\nFROM baz\n"); + my $map = $stmt->select_map; + is(scalar(keys %$map), 2); + is_deeply($map, {'count(foo)' => 'count1', 'count(bar)' => 'count2'}, 'right map'); +}; # HAVING $stmt = ns(); From 3767867c9232bfb8809acc9dc6683cb640c6bad5 Mon Sep 17 00:00:00 2001 From: Keita Jamadam Sugama Date: Thu, 11 Sep 2025 16:35:01 +0900 Subject: [PATCH 6/6] pass $dbh via as_sql parameter --- lib/Data/ObjectDriver/Driver/DBI.pm | 4 ++-- lib/Data/ObjectDriver/SQL.pm | 5 ++--- t/11-sql.t | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Data/ObjectDriver/Driver/DBI.pm b/lib/Data/ObjectDriver/Driver/DBI.pm index 7df6851..4977fa8 100644 --- a/lib/Data/ObjectDriver/Driver/DBI.pm +++ b/lib/Data/ObjectDriver/Driver/DBI.pm @@ -165,7 +165,7 @@ sub prepare_fetch { my $stmt = $driver->prepare_statement($class, $terms, $args); - my $sql = $stmt->as_sql; + my $sql = $stmt->as_sql($driver->rw_handle); $sql .= "\nFOR UPDATE" if $orig_args->{for_update}; return ($sql, $stmt->{bind}, $stmt) } @@ -711,7 +711,7 @@ sub prepare_statement { my($class, $terms, $args) = @_; my $dbd = $driver->dbd; - my $stmt = $args->{sql_statement} || $dbd->sql_class->new({dbh => $driver->rw_handle}); + my $stmt = $args->{sql_statement} || $dbd->sql_class->new; if (my $tbl = $driver->table_for($class)) { my $cols = $class->column_names; diff --git a/lib/Data/ObjectDriver/SQL.pm b/lib/Data/ObjectDriver/SQL.pm index 41ebce2..5ddfc7e 100644 --- a/lib/Data/ObjectDriver/SQL.pm +++ b/lib/Data/ObjectDriver/SQL.pm @@ -10,7 +10,7 @@ __PACKAGE__->mk_accessors(qw( select distinct select_map select_map_reverse from joins where bind limit offset group order having where_values column_mutator index_hint - comment dbh + comment )); sub new { @@ -58,7 +58,7 @@ sub add_index_hint { } sub as_sql { - my $stmt = shift; + my ($stmt, $dbh) = @_; my $sql = ''; if (@{ $stmt->select }) { $sql .= 'SELECT '; @@ -66,7 +66,6 @@ sub as_sql { $sql .= join(', ', map { my $alias = $stmt->select_map->{$_}; if ($alias && !/(?:^|\.)\Q$alias\E$/) { - my $dbh = $stmt->dbh; $alias = $dbh->quote_identifier($alias) if $dbh; qq!$_ $alias! } else { diff --git a/t/11-sql.t b/t/11-sql.t index 644f009..5d5c899 100644 --- a/t/11-sql.t +++ b/t/11-sql.t @@ -415,16 +415,16 @@ is( subtest 'quote can be used based on given dbh' => sub { use Wine; - $stmt = ns({dbh => Wine->driver->rw_handle}); + $stmt = ns(); $stmt->add_select(foo => 'bar'); @{$stmt->from} = ('baz'); - my $quoted = Wine->driver->dbh->quote_identifier('bar'); - is sql_normalize($stmt->as_sql), sql_normalize(<<"EOF"), 'right sql'; + my $quoted = Wine->driver->rw_handle->quote_identifier('bar'); + is sql_normalize($stmt->as_sql(Wine->driver->rw_handle)), sql_normalize(<<"EOF"), 'right sql'; SELECT foo $quoted FROM baz EOF }; -sub ns { Data::ObjectDriver::SQL->new(@_) } +sub ns { Data::ObjectDriver::SQL->new } sub sql_normalize { my $sql = shift;