| ... | ... |
@@ -1,3 +1,5 @@ |
| 1 |
+0.1667 |
|
| 2 |
+ - added EXPERIMENTAL reserved_word_quote attribute. |
|
| 1 | 3 |
0.1666 |
| 2 | 4 |
- removed from cache() and cache_method() document for a while and cache() value |
| 3 | 5 |
become 0 because I find something bug. |
| ... | ... |
@@ -55,6 +55,7 @@ __PACKAGE__->attr( |
| 55 | 55 |
models => sub { {} },
|
| 56 | 56 |
query_builder => sub { DBIx::Custom::QueryBuilder->new },
|
| 57 | 57 |
result_class => 'DBIx::Custom::Result', |
| 58 |
+ reserved_word_quote => '', |
|
| 58 | 59 |
safety_character => '\w', |
| 59 | 60 |
stash => sub { {} }
|
| 60 | 61 |
); |
| ... | ... |
@@ -147,8 +148,10 @@ sub column {
|
| 147 | 148 |
|
| 148 | 149 |
$columns ||= []; |
| 149 | 150 |
|
| 151 |
+ my $q = $self->reserved_word_quote; |
|
| 152 |
+ |
|
| 150 | 153 |
my @column; |
| 151 |
- push @column, "$table.$_ as ${table}__$_" for @$columns;
|
|
| 154 |
+ push @column, "$q$table$q.$q$_$q as $q${table}${q}__$q$_$q" for @$columns;
|
|
| 152 | 155 |
|
| 153 | 156 |
return join (', ', @column);
|
| 154 | 157 |
} |
| ... | ... |
@@ -195,6 +198,14 @@ sub create_query {
|
| 195 | 198 |
# Create query |
| 196 | 199 |
$query = $builder->build_query($source); |
| 197 | 200 |
|
| 201 |
+ # Bind |
|
| 202 |
+ my $columns = $query->columns; |
|
| 203 |
+ if (my $q = $self->reserved_word_quote) {
|
|
| 204 |
+ foreach my $column (@$columns) {
|
|
| 205 |
+ $column =~ s/$q//g; |
|
| 206 |
+ } |
|
| 207 |
+ } |
|
| 208 |
+ |
|
| 198 | 209 |
# Cache query |
| 199 | 210 |
$self->cache_method->($self, $source, |
| 200 | 211 |
{sql => $query->sql,
|
| ... | ... |
@@ -245,6 +256,9 @@ our %DELETE_ARGS |
| 245 | 256 |
|
| 246 | 257 |
sub delete {
|
| 247 | 258 |
my ($self, %args) = @_; |
| 259 |
+ |
|
| 260 |
+ # Quote for reserved word |
|
| 261 |
+ my $q = $self->reserved_word_quote; |
|
| 248 | 262 |
|
| 249 | 263 |
# Check argument names |
| 250 | 264 |
foreach my $name (keys %args) {
|
| ... | ... |
@@ -273,7 +287,7 @@ sub delete {
|
| 273 | 287 |
my @sql; |
| 274 | 288 |
|
| 275 | 289 |
# Delete |
| 276 |
- push @sql, "delete from $table $swhere"; |
|
| 290 |
+ push @sql, "delete from $q$table$q $swhere"; |
|
| 277 | 291 |
push @sql, $append if $append; |
| 278 | 292 |
|
| 279 | 293 |
my $sql = join(' ', @sql);
|
| ... | ... |
@@ -396,6 +410,9 @@ sub each_column {
|
| 396 | 410 |
sub execute{
|
| 397 | 411 |
my ($self, $query, %args) = @_; |
| 398 | 412 |
|
| 413 |
+ # Quote for reserved word |
|
| 414 |
+ my $q = $self->reserved_word_quote; |
|
| 415 |
+ |
|
| 399 | 416 |
# Check argument names |
| 400 | 417 |
foreach my $name (keys %args) {
|
| 401 | 418 |
croak qq{Argument "$name" is invalid name}
|
| ... | ... |
@@ -412,6 +429,11 @@ sub execute{
|
| 412 | 429 |
my $filter = {};
|
| 413 | 430 |
|
| 414 | 431 |
my $tables = $query->tables; |
| 432 |
+ if ($q) {
|
|
| 433 |
+ foreach my $table (@$tables) {
|
|
| 434 |
+ $table =~ s/$q//g; |
|
| 435 |
+ } |
|
| 436 |
+ } |
|
| 415 | 437 |
my $arg_tables = $args{table} || [];
|
| 416 | 438 |
$arg_tables = [$arg_tables] |
| 417 | 439 |
unless ref $arg_tables eq 'ARRAY'; |
| ... | ... |
@@ -534,6 +556,9 @@ our %INSERT_ARGS = map { $_ => 1 } @COMMON_ARGS, qw/param append/;
|
| 534 | 556 |
|
| 535 | 557 |
sub insert {
|
| 536 | 558 |
my ($self, %args) = @_; |
| 559 |
+ |
|
| 560 |
+ # Quote for reserved word |
|
| 561 |
+ my $q = $self->reserved_word_quote; |
|
| 537 | 562 |
|
| 538 | 563 |
# Check argument names |
| 539 | 564 |
foreach my $name (keys %args) {
|
| ... | ... |
@@ -553,6 +578,8 @@ sub insert {
|
| 553 | 578 |
foreach my $column (keys %$param) {
|
| 554 | 579 |
croak qq{"$column" is not safety column name}
|
| 555 | 580 |
unless $column =~ /^[$safety\.]+$/; |
| 581 |
+ $column = "$q$column$q"; |
|
| 582 |
+ $column =~ s/\./$q.$q/; |
|
| 556 | 583 |
push @columns, $column; |
| 557 | 584 |
} |
| 558 | 585 |
|
| ... | ... |
@@ -560,7 +587,7 @@ sub insert {
|
| 560 | 587 |
my @sql; |
| 561 | 588 |
|
| 562 | 589 |
# Insert |
| 563 |
- push @sql, "insert into $table {insert_param ". join(' ', @columns) . '}';
|
|
| 590 |
+ push @sql, "insert into $q$table$q {insert_param ". join(' ', @columns) . '}';
|
|
| 564 | 591 |
push @sql, $append if $append; |
| 565 | 592 |
|
| 566 | 593 |
# SQL |
| ... | ... |
@@ -734,9 +761,11 @@ sub model {
|
| 734 | 761 |
sub mycolumn {
|
| 735 | 762 |
my ($self, $table, $columns) = @_; |
| 736 | 763 |
|
| 764 |
+ my $q = $self->reserved_word_quote; |
|
| 765 |
+ |
|
| 737 | 766 |
$columns ||= []; |
| 738 | 767 |
my @column; |
| 739 |
- push @column, "$table.$_ as $_" for @$columns; |
|
| 768 |
+ push @column, "$q$table$q.$q$_$q as $q$_$q" for @$columns; |
|
| 740 | 769 |
|
| 741 | 770 |
return join (', ', @column);
|
| 742 | 771 |
} |
| ... | ... |
@@ -787,6 +816,9 @@ our %SELECT_ARGS |
| 787 | 816 |
|
| 788 | 817 |
sub select {
|
| 789 | 818 |
my ($self, %args) = @_; |
| 819 |
+ |
|
| 820 |
+ # Quote for reserved word |
|
| 821 |
+ my $q = $self->reserved_word_quote; |
|
| 790 | 822 |
|
| 791 | 823 |
# Check argument names |
| 792 | 824 |
foreach my $name (keys %args) {
|
| ... | ... |
@@ -876,13 +908,13 @@ sub select {
|
| 876 | 908 |
if ($relation) {
|
| 877 | 909 |
my $found = {};
|
| 878 | 910 |
foreach my $table (@$tables) {
|
| 879 |
- push @sql, ($table, ',') unless $found->{$table};
|
|
| 911 |
+ push @sql, ("$q$table$q", ',') unless $found->{$table};
|
|
| 880 | 912 |
$found->{$table} = 1;
|
| 881 | 913 |
} |
| 882 | 914 |
} |
| 883 | 915 |
else {
|
| 884 | 916 |
my $main_table = $tables->[-1] || ''; |
| 885 |
- push @sql, $main_table; |
|
| 917 |
+ push @sql, "$q$main_table$q"; |
|
| 886 | 918 |
} |
| 887 | 919 |
pop @sql if ($sql[-1] || '') eq ','; |
| 888 | 920 |
|
| ... | ... |
@@ -993,6 +1025,9 @@ our %UPDATE_ARGS |
| 993 | 1025 |
|
| 994 | 1026 |
sub update {
|
| 995 | 1027 |
my ($self, %args) = @_; |
| 1028 |
+ |
|
| 1029 |
+ # Quote for reserved word |
|
| 1030 |
+ my $q = $self->reserved_word_quote; |
|
| 996 | 1031 |
|
| 997 | 1032 |
# Check argument names |
| 998 | 1033 |
foreach my $name (keys %args) {
|
| ... | ... |
@@ -1008,20 +1043,19 @@ sub update {
|
| 1008 | 1043 |
my $append = delete $args{append} || '';
|
| 1009 | 1044 |
my $allow_update_all = delete $args{allow_update_all};
|
| 1010 | 1045 |
|
| 1011 |
- # Update keys |
|
| 1012 |
- my @clumns = keys %$param; |
|
| 1013 |
- |
|
| 1014 | 1046 |
# Columns |
| 1015 | 1047 |
my @columns; |
| 1016 | 1048 |
my $safety = $self->safety_character; |
| 1017 | 1049 |
foreach my $column (keys %$param) {
|
| 1018 | 1050 |
croak qq{"$column" is not safety column name}
|
| 1019 | 1051 |
unless $column =~ /^[$safety\.]+$/; |
| 1020 |
- push @columns, $column; |
|
| 1052 |
+ $column = "$q$column$q"; |
|
| 1053 |
+ $column =~ s/\./$q.$q/; |
|
| 1054 |
+ push @columns, "$column"; |
|
| 1021 | 1055 |
} |
| 1022 | 1056 |
|
| 1023 | 1057 |
# Update clause |
| 1024 |
- my $update_clause = '{update_param ' . join(' ', @clumns) . '}';
|
|
| 1058 |
+ my $update_clause = '{update_param ' . join(' ', @columns) . '}';
|
|
| 1025 | 1059 |
|
| 1026 | 1060 |
# Where |
| 1027 | 1061 |
my $w = $self->_where($where); |
| ... | ... |
@@ -1037,7 +1071,7 @@ sub update {
|
| 1037 | 1071 |
my @sql; |
| 1038 | 1072 |
|
| 1039 | 1073 |
# Update |
| 1040 |
- push @sql, "update $table $update_clause $swhere"; |
|
| 1074 |
+ push @sql, "update $q$table$q $update_clause $swhere"; |
|
| 1041 | 1075 |
push @sql, $append if $append; |
| 1042 | 1076 |
|
| 1043 | 1077 |
# Rearrange parameters |
| ... | ... |
@@ -1138,6 +1172,7 @@ sub where {
|
| 1138 | 1172 |
return DBIx::Custom::Where->new( |
| 1139 | 1173 |
query_builder => $self->query_builder, |
| 1140 | 1174 |
safety_character => $self->safety_character, |
| 1175 |
+ reserved_word_quote => $self->reserved_word_quote, |
|
| 1141 | 1176 |
@_ |
| 1142 | 1177 |
); |
| 1143 | 1178 |
} |
| ... | ... |
@@ -1254,8 +1289,12 @@ sub _tables {
|
| 1254 | 1289 |
my $tables = []; |
| 1255 | 1290 |
|
| 1256 | 1291 |
my $safety_character = $self->safety_character; |
| 1292 |
+ my $q = $self->reserved_word_quote; |
|
| 1293 |
+ my $q_re = quotemeta($q); |
|
| 1257 | 1294 |
|
| 1258 |
- while ($source =~ /\b($safety_character+)\./g) {
|
|
| 1295 |
+ my $table_re = $q ? qr/\b$q_re?([$safety_character]+)$q_re?\./ |
|
| 1296 |
+ : qr/\b([$safety_character]+)\./; |
|
| 1297 |
+ while ($source =~ /$table_re/g) {
|
|
| 1259 | 1298 |
push @$tables, $1; |
| 1260 | 1299 |
} |
| 1261 | 1300 |
|
| ... | ... |
@@ -1267,13 +1306,17 @@ sub _push_join {
|
| 1267 | 1306 |
|
| 1268 | 1307 |
return unless @$join; |
| 1269 | 1308 |
|
| 1309 |
+ my $q = $self->reserved_word_quote; |
|
| 1310 |
+ |
|
| 1270 | 1311 |
my $tree = {};
|
| 1271 | 1312 |
|
| 1272 | 1313 |
for (my $i = 0; $i < @$join; $i++) {
|
| 1273 | 1314 |
|
| 1274 | 1315 |
my $join_clause = $join->[$i]; |
| 1275 |
- |
|
| 1276 |
- if ($join_clause =~ /\s([^\.\s]+?)\..+\s([^\.\s]+?)\..+?$/) {
|
|
| 1316 |
+ my $q_re = quotemeta($q); |
|
| 1317 |
+ my $join_re = $q ? qr/\s$q_re?([^\.\s$q_re]+?)$q_re?\..+\s$q_re?([^\.\s$q_re]+?)$q_re?\..+?$/ |
|
| 1318 |
+ : qr/\s([^\.\s]+?)\..+\s([^\.\s]+?)\..+?$/; |
|
| 1319 |
+ if ($join_clause =~ $join_re) {
|
|
| 1277 | 1320 |
|
| 1278 | 1321 |
my $table1 = $1; |
| 1279 | 1322 |
my $table2 = $2; |
| ... | ... |
@@ -1305,7 +1348,13 @@ sub _where {
|
| 1305 | 1348 |
my $w; |
| 1306 | 1349 |
if (ref $where eq 'HASH') {
|
| 1307 | 1350 |
my $clause = ['and']; |
| 1308 |
- push @$clause, "{= $_}" for keys %$where;
|
|
| 1351 |
+ my $q = $self->reserved_word_quote; |
|
| 1352 |
+ foreach my $column (keys %$where) {
|
|
| 1353 |
+ $column = "$q$column$q"; |
|
| 1354 |
+ $column =~ s/\./$q.$q/; |
|
| 1355 |
+ push @$clause, "{= $column}" for keys %$where;
|
|
| 1356 |
+ } |
|
| 1357 |
+ |
|
| 1309 | 1358 |
$w = $self->where(clause => $clause, param => $where); |
| 1310 | 1359 |
} |
| 1311 | 1360 |
elsif (ref $where eq 'DBIx::Custom::Where') {
|
| ... | ... |
@@ -1596,6 +1645,13 @@ Password, used when C<connect()> is executed. |
| 1596 | 1645 |
|
| 1597 | 1646 |
Query builder, default to L<DBIx::Custom::QueryBuilder> object. |
| 1598 | 1647 |
|
| 1648 |
+=head2 C<reserved_word_quote> EXPERIMENTAL |
|
| 1649 |
+ |
|
| 1650 |
+ my reserved_word_quote = $dbi->reserved_word_quote; |
|
| 1651 |
+ $dbi = $dbi->reserved_word_quote('"');
|
|
| 1652 |
+ |
|
| 1653 |
+Quote for reserved word, default to empty string. |
|
| 1654 |
+ |
|
| 1599 | 1655 |
=head2 C<result_class> |
| 1600 | 1656 |
|
| 1601 | 1657 |
my $result_class = $dbi->result_class; |
| ... | ... |
@@ -16,6 +16,7 @@ push @DBIx::Custom::CARP_NOT, __PACKAGE__; |
| 16 | 16 |
__PACKAGE__->attr( |
| 17 | 17 |
[qw/param query_builder safety_character/], |
| 18 | 18 |
clause => sub { [] },
|
| 19 |
+ reserved_word_quote => '' |
|
| 19 | 20 |
); |
| 20 | 21 |
|
| 21 | 22 |
sub to_string {
|
| ... | ... |
@@ -75,6 +76,10 @@ sub _parse {
|
| 75 | 76 |
croak qq{Each tag contains one column name: tag "$clause"}
|
| 76 | 77 |
unless @$columns == 1; |
| 77 | 78 |
my $column = $columns->[0]; |
| 79 |
+ if (my $q = $self->reserved_word_quote) {
|
|
| 80 |
+ $column =~ s/$q//g; |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 78 | 83 |
my $safety = $self->safety_character; |
| 79 | 84 |
croak qq{"$column" is not safety column name}
|
| 80 | 85 |
unless $column =~ /^[$safety\.]+$/; |
| ... | ... |
@@ -242,6 +242,15 @@ like($@, qr/noexist/, "invalid"); |
| 242 | 242 |
eval{$dbi->insert(table => 'table', param => {';' => 1})};
|
| 243 | 243 |
like($@, qr/safety/); |
| 244 | 244 |
|
| 245 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 246 |
+$dbi->reserved_word_quote('"');
|
|
| 247 |
+$dbi->execute('create table "table" ("select")');
|
|
| 248 |
+$dbi->apply_filter('table', select => {out => sub { $_[0] * 2}});
|
|
| 249 |
+$dbi->insert(table => 'table', param => {select => 1});
|
|
| 250 |
+$result = $dbi->execute('select * from "table"');
|
|
| 251 |
+$rows = $result->fetch_hash_all; |
|
| 252 |
+is_deeply($rows, [{select => 2}], "reserved word");
|
|
| 253 |
+ |
|
| 245 | 254 |
test 'update'; |
| 246 | 255 |
$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
| 247 | 256 |
$dbi->execute($CREATE_TABLE->{1});
|
| ... | ... |
@@ -331,6 +340,31 @@ like($@, qr/safety/); |
| 331 | 340 |
eval{$dbi->update(table => 'table1', param => {'key1' => 1}, where => {';' => 1})};
|
| 332 | 341 |
like($@, qr/safety/); |
| 333 | 342 |
|
| 343 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 344 |
+$dbi->reserved_word_quote('"');
|
|
| 345 |
+$dbi->execute('create table "table" ("select", "update")');
|
|
| 346 |
+$dbi->apply_filter('table', select => {out => sub { $_[0] * 2}});
|
|
| 347 |
+$dbi->apply_filter('table', update => {out => sub { $_[0] * 3}});
|
|
| 348 |
+$dbi->insert(table => 'table', param => {select => 1});
|
|
| 349 |
+$dbi->update(table => 'table', where => {select => 1}, param => {update => 2});
|
|
| 350 |
+$result = $dbi->execute('select * from "table"');
|
|
| 351 |
+$rows = $result->fetch_hash_all; |
|
| 352 |
+is_deeply($rows, [{select => 2, update => 6}], "reserved word");
|
|
| 353 |
+ |
|
| 354 |
+eval {$dbi->update_all(table => 'table', param => {';' => 2}) };
|
|
| 355 |
+like($@, qr/safety/); |
|
| 356 |
+ |
|
| 357 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 358 |
+$dbi->reserved_word_quote('"');
|
|
| 359 |
+$dbi->execute('create table "table" ("select", "update")');
|
|
| 360 |
+$dbi->apply_filter('table', select => {out => sub { $_[0] * 2}});
|
|
| 361 |
+$dbi->apply_filter('table', update => {out => sub { $_[0] * 3}});
|
|
| 362 |
+$dbi->insert(table => 'table', param => {select => 1});
|
|
| 363 |
+$dbi->update(table => 'table', where => {'table.select' => 1}, param => {update => 2});
|
|
| 364 |
+$result = $dbi->execute('select * from "table"');
|
|
| 365 |
+$rows = $result->fetch_hash_all; |
|
| 366 |
+is_deeply($rows, [{select => 2, update => 6}], "reserved word");
|
|
| 367 |
+ |
|
| 334 | 368 |
test 'update_all'; |
| 335 | 369 |
$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
| 336 | 370 |
$dbi->execute($CREATE_TABLE->{1});
|
| ... | ... |
@@ -411,6 +445,16 @@ like($@, qr/"where" must be specified/, |
| 411 | 445 |
eval{$dbi->delete(table => 'table1', where => {';' => 1})};
|
| 412 | 446 |
like($@, qr/safety/); |
| 413 | 447 |
|
| 448 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 449 |
+$dbi->reserved_word_quote('"');
|
|
| 450 |
+$dbi->execute('create table "table" ("select", "update")');
|
|
| 451 |
+$dbi->apply_filter('table', select => {out => sub { $_[0] * 2}});
|
|
| 452 |
+$dbi->insert(table => 'table', param => {select => 1});
|
|
| 453 |
+$dbi->delete(table => 'table', where => {select => 1});
|
|
| 454 |
+$result = $dbi->execute('select * from "table"');
|
|
| 455 |
+$rows = $result->fetch_hash_all; |
|
| 456 |
+is_deeply($rows, [], "reserved word"); |
|
| 457 |
+ |
|
| 414 | 458 |
test 'delete_all'; |
| 415 | 459 |
$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
| 416 | 460 |
$dbi->execute($CREATE_TABLE->{0});
|
| ... | ... |
@@ -468,6 +512,14 @@ is_deeply($rows, [{table1_key1 => 1, table2_key1 => 1, key2 => 2, key3 => 5}], "
|
| 468 | 512 |
eval{$dbi->select(table => 'table1', noexist => 1)};
|
| 469 | 513 |
like($@, qr/noexist/, "invalid"); |
| 470 | 514 |
|
| 515 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 516 |
+$dbi->reserved_word_quote('"');
|
|
| 517 |
+$dbi->execute('create table "table" ("select", "update")');
|
|
| 518 |
+$dbi->apply_filter('table', select => {out => sub { $_[0] * 2}});
|
|
| 519 |
+$dbi->insert(table => 'table', param => {select => 1, update => 2});
|
|
| 520 |
+$result = $dbi->select(table => 'table', where => {select => 1});
|
|
| 521 |
+$rows = $result->fetch_hash_all; |
|
| 522 |
+is_deeply($rows, [{select => 2, update => 2}], "reserved word");
|
|
| 471 | 523 |
|
| 472 | 524 |
test 'fetch filter'; |
| 473 | 525 |
$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
| ... | ... |
@@ -1816,6 +1868,20 @@ $rows = $dbi->select( |
| 1816 | 1868 |
)->fetch_hash_all; |
| 1817 | 1869 |
is_deeply($rows, [{table1__key1 => 1}]);
|
| 1818 | 1870 |
|
| 1871 |
+$dbi = DBIx::Custom->connect($NEW_ARGS->{0});
|
|
| 1872 |
+$dbi->reserved_word_quote('"');
|
|
| 1873 |
+$dbi->execute($CREATE_TABLE->{0});
|
|
| 1874 |
+$dbi->insert(table => 'table1', param => {key1 => 1, key2 => 2});
|
|
| 1875 |
+$dbi->execute($CREATE_TABLE->{2});
|
|
| 1876 |
+$dbi->insert(table => 'table2', param => {key1 => 1, key3 => 5});
|
|
| 1877 |
+$rows = $dbi->select( |
|
| 1878 |
+ table => 'table1', |
|
| 1879 |
+ column => '"table1"."key1" as "table1_key1", "table2"."key1" as "table2_key1", "key2", "key3"', |
|
| 1880 |
+ where => {'table1.key2' => 2},
|
|
| 1881 |
+ join => ['left outer join "table2" on "table1"."key1" = "table2"."key1"'], |
|
| 1882 |
+)->fetch_hash_all; |
|
| 1883 |
+is_deeply($rows, [{table1_key1 => 1, table2_key1 => 1, key2 => 2, key3 => 5}],
|
|
| 1884 |
+ 'reserved_word_quote'); |
|
| 1819 | 1885 |
|
| 1820 | 1886 |
test 'model join and column attribute and all_column option'; |
| 1821 | 1887 |
{
|