2f08e0fe86eccb22ace4e9c1e34f1763cc3d4d34
Perl/DBIx-NamedParams.md
... | ... | @@ -1,6 +1,8 @@ |
1 | 1 | [[_TOC_]] |
2 | 2 | |
3 | 3 | # 概要 |
4 | +- [DBIx::NamedParams - metacpan.org](https://metacpan.org/pod/DBIx::NamedParams) |
|
5 | +- [p-DBIx-NamedParams - GitHub](https://github.com/TakeAsh/p-DBIx-NamedParams) |
|
4 | 6 | - mysql でも prepare でプレースホルダとして ? じゃなく名前で指定できるようにする。 |
5 | 7 | - 同じ名前を繰り返し使ってもエラーにならない。 |
6 | 8 | - こっちより [CPAN:DBIx-Custom](http://search.cpan.org/dist/DBIx-Custom) の方が柔軟で高機能だなー。どうしたもんか。 |
... | ... | @@ -82,210 +84,6 @@ my %DrvTypeToSQLType = $dbh->driver_typename_map(); |
82 | 84 | - prepare_ex のときに @_NamedParams をクリアするようにした。 |
83 | 85 | |
84 | 86 | # ソース |
85 | -- [NamedParams.zip](NamedParams.zip) |
|
86 | - |
|
87 | -## NamedParams.pm |
|
88 | -```perl |
|
89 | -# DBI::db::prepare, DBI::st::bind_param を拡張する。 |
|
90 | -# http://www.TakeAsh.net/wiki/?Perl/DBIx-NamedParams |
|
91 | -# |
|
92 | -# prepare_ex では「:名前-型」形式のようにプレースホルダを書く。 |
|
93 | -# bind_param_ex では名前と値の対応を示すハッシュを渡す。 |
|
94 | - |
|
95 | -package DBIx::NamedParams; |
|
96 | - |
|
97 | -use strict; |
|
98 | -use warnings; |
|
99 | -use utf8; |
|
100 | -use Encode; |
|
101 | -use Carp qw( croak ); |
|
102 | -use parent qw( DBI ); |
|
103 | -use DBI::Const::GetInfoType; |
|
104 | -use Log::Dispatch; |
|
105 | -use POSIX qw( strftime ); |
|
106 | - |
|
107 | -use version; our $VERSION = qv( '0.0.5' ); |
|
108 | - |
|
109 | -( $ENV{'LANG'} || '' ) =~ /\.(.*)$/; # ja_JP.UTF-8 |
|
110 | -my $charsetConsole = $1 || 'CP932'; |
|
111 | -my $charsetFile = 'UTF-8'; |
|
112 | - |
|
113 | -my $_default_log_filename = ( $ENV{'HOME'} || $ENV{'USERPROFILE'} ); |
|
114 | -$_default_log_filename =~ s#\\#/#g; |
|
115 | -$_default_log_filename .= '/DBIx-NamedParams.log'; |
|
116 | - |
|
117 | -my %_SQL_TypeRefs = (); |
|
118 | -my %_SQL_TypeInvs = (); |
|
119 | -my $_SQL_Types = ""; |
|
120 | -my @_NamedParams = (); |
|
121 | -my $_index; |
|
122 | -my $_log = undef; |
|
123 | - |
|
124 | -sub import { |
|
125 | - DBI->import(); # 不要か? |
|
126 | - _init(); |
|
127 | - *{DBI::db::driver_typename_map} = \&driver_typename_map; |
|
128 | - *{DBI::db::prepare_ex} = \&prepare_ex; |
|
129 | - *{DBI::st::bind_param_ex} = \&bind_param_ex; |
|
130 | -} |
|
131 | - |
|
132 | -sub _init |
|
133 | -{ |
|
134 | - foreach( @{ $DBI::EXPORT_TAGS{sql_types} } ){ |
|
135 | - my $refFunc = \&{"DBI::$_"}; |
|
136 | - if ( /^SQL_(.*)$/i ){ |
|
137 | - $_SQL_TypeRefs{ $1 } = &{ $refFunc }; |
|
138 | - $_SQL_TypeInvs{ &{ $refFunc } } = $1; |
|
139 | - } |
|
140 | - } |
|
141 | - $_SQL_Types = all_sql_types(); |
|
142 | -} |
|
143 | - |
|
144 | -sub debug_log |
|
145 | -{ |
|
146 | - my( $filename ) = @_; |
|
147 | - $filename = encode( $charsetConsole, ( $filename || $_default_log_filename ) ); |
|
148 | - $_log = Log::Dispatch->new( |
|
149 | - outputs => [ [ |
|
150 | - 'File', |
|
151 | - min_level => 'debug', |
|
152 | - filename => $filename, |
|
153 | - binmode => ":encoding($charsetFile)", |
|
154 | - permissions => 0666, |
|
155 | - newline => 1, |
|
156 | - ], ], |
|
157 | - ); |
|
158 | - $_log->info( _thisFuncName(), strftime( "%Y-%m-%d %H:%M:%S", localtime ) ); |
|
159 | -} |
|
160 | - |
|
161 | -sub _thisFuncName |
|
162 | -{ |
|
163 | - ( caller( 1 ) )[3] =~ /([^:]+)$/; |
|
164 | - return $1; |
|
165 | -} |
|
166 | - |
|
167 | -sub all_sql_types |
|
168 | -{ |
|
169 | - return wantarray |
|
170 | - ? sort( keys( %_SQL_TypeRefs ) ) |
|
171 | - : join( "|", sort( keys( %_SQL_TypeRefs ) ) ); |
|
172 | -} |
|
173 | -sub driver_typename_map |
|
174 | -{ |
|
175 | - my( $self ) = @_; |
|
176 | - my %map = (); |
|
177 | - foreach my $refType ( $self->type_info() ){ |
|
178 | - my $datatype = $refType->{ 'SQL_DATA_TYPE' } # MS SQL Server |
|
179 | - || $refType->{ 'SQL_DATATYPE' }; # MySQL |
|
180 | - $map{ $refType->{ 'TYPE_NAME' } } = $_SQL_TypeInvs{ $datatype } || 'WVARCHAR'; |
|
181 | - } |
|
182 | - if ( $self->get_info( $GetInfoType{ 'SQL_DBMS_NAME' } ) eq 'Microsoft SQL Server' ){ |
|
183 | - $map{ 'datetime' } = 'WVARCHAR'; |
|
184 | - $map{ 'smalldatetime' } = 'WVARCHAR'; |
|
185 | - } |
|
186 | - return %map; |
|
187 | -} |
|
188 | - |
|
189 | -sub prepare_ex |
|
190 | -{ |
|
191 | - my( $self, $sqlex, $refHash ) = @_; |
|
192 | - my $ret = undef; |
|
193 | - my $validHash = defined( $refHash ) && ref( $refHash ) eq 'HASH'; |
|
194 | - if ( $sqlex =~ /\:([\w]+)\+-($_SQL_Types)\b/ ){ |
|
195 | - if ( $validHash ){ |
|
196 | - $sqlex =~ s/\:([\w]+)\+-($_SQL_Types)\b/_parse_ex1($refHash,$1,$2);/ge; |
|
197 | - } else { |
|
198 | - croak( "prepare_ex need a hash reference when SQL is variable length.\n" ); |
|
199 | - } |
|
200 | - } |
|
201 | - @_NamedParams = (); |
|
202 | - $_index = 1; |
|
203 | - $sqlex =~ s/\:([\w]+)(?:\{(\d+)\})?-($_SQL_Types)\b/_parse_ex2($1,$2,$3);/ge; |
|
204 | - if ( $_log ){ |
|
205 | - $_log->info( _thisFuncName(), 'sql_raw', "{{$sqlex}}" ); |
|
206 | - } |
|
207 | - $ret = $self->prepare( $sqlex ) or croak( "$DBI::errstr" ); |
|
208 | - if ( $validHash ){ |
|
209 | - $ret->bind_param_ex( $refHash ); |
|
210 | - } |
|
211 | - return $ret; |
|
212 | -} |
|
213 | - |
|
214 | -sub _parse_ex1 |
|
215 | -{ |
|
216 | - my( $refHash, $name, $type ) = @_; |
|
217 | - return ":${name}{" . scalar( @{ $refHash->{ $name } } ) . "}-$type"; |
|
218 | -} |
|
219 | - |
|
220 | -sub _parse_ex2 |
|
221 | -{ |
|
222 | - my $name = shift || ''; |
|
223 | - my $repeat = shift || 0; |
|
224 | - my $type = shift || ''; |
|
225 | - my $ret = ''; |
|
226 | - # '' や 0 も有効値として使いたい場合 (Perl 5.9 以降) |
|
227 | - # my $param = shift // -1; |
|
228 | - if ( $_log ){ |
|
229 | - $_log->info( |
|
230 | - _thisFuncName(), |
|
231 | - "[$_index]", |
|
232 | - "\"$name\"", |
|
233 | - (!$repeat) ? "scalar" : "array[$repeat]", |
|
234 | - $type |
|
235 | - ); |
|
236 | - } |
|
237 | - if ( !$repeat ){ |
|
238 | - # scalar |
|
239 | - $_NamedParams[ $_index++ ] = { |
|
240 | - Name => $name, |
|
241 | - Type => $_SQL_TypeRefs{ $type }, |
|
242 | - Array => -1, |
|
243 | - }; |
|
244 | - $ret = '?'; |
|
245 | - } else { |
|
246 | - # array |
|
247 | - for( my $i=0; $i<$repeat; ++$i ){ |
|
248 | - $_NamedParams[ $_index++ ] = { |
|
249 | - Name => $name, |
|
250 | - Type => $_SQL_TypeRefs{ $type }, |
|
251 | - Array => $i, |
|
252 | - }; |
|
253 | - } |
|
254 | - $ret = substr( '?,' x $repeat, 0, -1 ); |
|
255 | - } |
|
256 | - return $ret; |
|
257 | -} |
|
258 | - |
|
259 | -sub bind_param_ex |
|
260 | -{ |
|
261 | - no warnings 'uninitialized'; |
|
262 | - my( $self, $refHash ) = @_; |
|
263 | - if ( !defined( $refHash ) || ref( $refHash ) ne 'HASH' ){ |
|
264 | - croak( "bind_param_ex need a hash reference.\n" ); |
|
265 | - } |
|
266 | - my $thisFunc = _thisFuncName(); |
|
267 | - for( my $i=1; $i<@_NamedParams; ++$i ){ |
|
268 | - my $idx = $_NamedParams[ $i ]{ 'Array' }; |
|
269 | - my $value1 = $refHash->{ $_NamedParams[ $i ]{ 'Name' } }; |
|
270 | - my $value2 = ( $idx < 0 || ref( $value1 ) ne 'ARRAY' ) |
|
271 | - ? $value1 |
|
272 | - : @{ $value1 }[ $idx ]; |
|
273 | - my $datatype = $_NamedParams[ $i ]{ 'Type' }; |
|
274 | - if ( $_log ){ |
|
275 | - $_log->info( |
|
276 | - $thisFunc, "[$i]", "\"$value2\"", $_SQL_TypeInvs{ $datatype } |
|
277 | - ); |
|
278 | - } |
|
279 | - $self->bind_param( $i, $value2 , { TYPE => $datatype } ) |
|
280 | - or croak( "$DBI::errstr\n" ); |
|
281 | - } |
|
282 | - return $self; |
|
283 | -} |
|
284 | - |
|
285 | -1; |
|
286 | - |
|
287 | -# EOF |
|
288 | -``` |
|
289 | 87 | |
290 | 88 | ## testNamedParams_MySQL.pl |
291 | 89 | ```perl |