Showing 4 changed files with 145 additions and 16 deletions
+2
Changes
... ...
@@ -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.
+72 -16
lib/DBIx/Custom.pm
... ...
@@ -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;
+5
lib/DBIx/Custom/Where.pm
... ...
@@ -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\.]+$/;
+66
t/dbix-custom-core-sqlite.t
... ...
@@ -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
 {