File Coverage

blib/lib/Class/DBI/Plugin/Backtickify.pm
Criterion Covered Total %
statement 16 64 25.0
branch 0 8 0.0
condition 0 20 0.0
subroutine 5 9 55.5
pod 1 1 100.0
total 22 102 21.5


line stmt bran cond sub pod time code
1             package Class::DBI::Plugin::Backtickify;
2              
3 1     1   166193 use warnings;
  1         2  
  1         41  
4 1     1   7 use strict;
  1         2  
  1         37  
5              
6 1     1   1050 use Class::ISA;
  1         4121  
  1         85  
7              
8             our $VERSION = 0.02;
9              
10             =head1 NAME
11              
12             Class::DBI::Plugin::Backtickify - surround column and table names with backticks
13              
14             =head1 SYNOPSIS
15              
16             package Film;
17             use base qw( Class::DBI );
18             use Class::DBI::Plugin::Backtickify; # must come after the use base
19            
20             =head1 DESCRIPTION
21              
22             Puts backticks around table and column names. This allows reserved words to be used
23             as column (and table?) names in MySQL (and others?).
24              
25             =head1 CAVEATS
26              
27             It works by installing a C method into your CDBI class. Other modules and
28             plugins maybe do the same thing, in which case they may not play nicely with this. It does
29             go through some hoops however to try and call other C methods, but all the
30             replacement tags will already have been removed so this might not help anyway. YMMV.
31              
32             The installed C finds column names using a regex over each C<@args> passed in. If
33             strings matching column names (but not supposed to represent column names) exist as words
34             in the input to the method, they will also get wrapped. Not sure how likely this is.
35              
36             I haven't tested if this works with joins, but it should.
37              
38             No tests yet.
39              
40             =cut
41              
42             sub import
43             {
44 1     1   12 my ( $class ) = @_;
45            
46 1         4 my $caller = caller( 0 );
47            
48 1     1   8 no strict 'refs';
  1         1  
  1         1119  
49 1         3 *{"$caller\::transform_sql"} = \&transform_sql;
  1         18  
50             }
51              
52             =head1 METHODS
53              
54             =over 4
55              
56             =item transform_sql
57              
58             =back
59              
60             =cut
61              
62             sub transform_sql
63             {
64 0     0 1   my ( $self, $sql, @args ) = @_;
65            
66             #warn "TRANSFORM_SQL: SQL IN: $sql - @args\n";
67            
68             # Each entry in @args is a SQL fragment. This will bugger with fragments that
69             # contain strings that match column names but are not supposed to be column names.
70 0     0     my $backtickify_arg = sub { $_[0] =~ s/\b$_\b/`$_`/g for $self->all_columns };
  0            
71 0           $backtickify_arg->( $_ ) for @args;
72            
73             # -------------------
74 0           my %cmap;
75             my $expand_table = sub {
76 0     0     my ($class, $alias) = split /=/, shift, 2;
77 0 0         my $table = $class ? $class->table : $self->table;
78 0   0       $cmap{ $alias || $table } = $class || ref $self || $self;
      0        
79 0   0       ($alias ||= "") &&= " AS `$alias`";
      0        
80 0           return "`$table`$alias";
81 0           };
82            
83             # -------------------
84             my $expand_join = sub {
85 0     0     my $joins = shift;
86 0           my @table = split /\s+/, $joins;
87 0           my %tojoin = map { $table[$_] => $table[ $_ + 1 ] } 0 .. $#table - 1;
  0            
88 0           my @sql;
89 0           while (my ($t1, $t2) = each %tojoin) {
90 0   0       my ($c1, $c2) = map $cmap{$_}
91             || $self->_croak("Don't understand table '$_' in JOIN"), ($t1, $t2);
92              
93             my $join_col = sub {
94 0           my ($c1, $c2) = @_;
95 0           my $meta = $c1->meta_info('has_a');
96 0           my ($col) = grep $meta->{$_}->foreign_class eq $c2, keys %$meta;
97 0           $col;
98 0           };
99              
100 0   0       my $col = $join_col->($c1 => $c2) || do {
101             ($c1, $c2) = ($c2, $c1);
102             ($t1, $t2) = ($t2, $t1);
103             $join_col->($c1 => $c2);
104             };
105              
106 0 0         $self->_croak("Don't know how to join $c1 to $c2") unless $col;
107 0           push @sql, sprintf " `%s`.`%s` = `%s`.`%s` ", $t1, $col, $t2,
108             $c2->primary_column;
109             }
110 0           return join " AND ", @sql;
111 0           };
112            
113             # -------------------
114 0           $sql =~ s/__TABLE\(?(.*?)\)?__/$expand_table->($1)/eg;
  0            
115 0           $sql =~ s/__JOIN\((.*?)\)__/$expand_join->($1)/eg;
  0            
116 0           $sql =~ s/__ESSENTIAL__/join ", ", map { "`$_`" } $self->_essential/eg;
  0            
  0            
117 0           $sql =~ s/__ESSENTIAL\((.*?)\)__/join ", ", map { "`$1`.`$_`" } $self->_essential/eg;
  0            
  0            
118            
119 0 0         if ( $sql =~ /__IDENTIFIER__/ )
120             {
121 0           my $key_sql = join " AND ", map "`$_`=?", $self->primary_columns;
122 0           $sql =~ s/__IDENTIFIER__/$key_sql/g;
123             }
124            
125             # nasty hack
126 0   0       my $super = ( Class::ISA::super_path( ref( $self ) || $self ) )[0];
127            
128 0           my $eval = '{ package %s; $self->SUPER::transform_sql( q(%s), ';
129 0           $eval .= 'q(%s), ' for @args;
130 0           $eval .= ') }';
131            
132 0           my $return = eval sprintf $eval, $super, $sql, @args;
133            
134 0 0         die $@ if $@;
135            
136 0           return $return;
137              
138             #my $out = $self->SUPER::transform_sql($sql => @args);
139             #warn "TRANSFORM_SQL: SQL OUT: $out\n";
140             #return $out;
141             }
142              
143              
144             =head1 AUTHOR
145              
146             David Baird, C<< >>
147              
148             =head1 BUGS
149              
150             Please report any bugs or feature requests to
151             C, or through the web interface at
152             L.
153             I will be notified, and then you'll automatically be notified of progress on
154             your bug as I make changes.
155              
156             =head1 ACKNOWLEDGEMENTS
157              
158             =head1 COPYRIGHT & LICENSE
159              
160             Copyright 2005 David Baird, All Rights Reserved.
161              
162             This program is free software; you can redistribute it and/or modify it
163             under the same terms as Perl itself.
164              
165             =cut
166              
167             1; # End of Class::DBI::Plugin::Backtickify