Line # Revision Author
1 3 ahitrov@rambler.ru package SQL::Common;
2 use base qw(Exporter);
3
4 use strict;
5 use Utils;
6 use Contenido::DateTime;
7 use Data::Dumper;
8
9 our @EXPORT = qw(&NIL);
10
11 sub NIL {
12 return '___NEXT___';
13 }
14
15 my %_TEXT_CONDITIONS = (
16 '!=' => 1,
17 '<>' => 1,
18 'NOT' => 1,
19 'LIKE' => 1,
20 'ILIKE' => 1,
21 'NOT LIKE' => 1,
22 'NOT ILIKE' => 1,
23 '~' => 1,
24 );
25 my %_CONDITIONS = (
26 'NOT' => 1,
27 '!=' => 1,
28 '<>' => 1,
29 '<' => 1,
30 '>' => 1,
31 '>=' => 1,
32 '<=' => 1,
33 );
34 my %_DATE_CONDITIONS = (
35 '!=' => 1,
36 '<>' => 1,
37 '<' => 1,
38 '>' => 1,
39 '>=' => 1,
40 '<=' => 1,
41 );
42
43 sub _generic_int_filter {
44 my ($field,$value,$negation)=@_;
45
46 my $mode = $negation ? 'NOT IN':'IN';
47
48 #undef <=> NULL
49 if ( !defined($value) ) {
50 $mode = $negation ? 'IS NOT' : 'IS';
51 return "$field $mode NULL", [];
52 } elsif ( (ref($value) eq 'ARRAY') and @$value ) {
53 my $values_string = '?, 'x(scalar (@$value)-1).'?';
54 return " ($field $mode ($values_string)) ", [@$value];
55 } elsif (ref($value) eq 'ARRAY') {
56 # skip empty array
57 return ($negation ? ' TRUE ' : ' FALSE '), [];
58 } elsif ( $value eq 'positive' || $value eq 'negative' || $value eq 'natural' ) {
59 $mode = $value eq 'positive' ? '>' : $value eq 'negative' ? '<' : '>=';
60 return "$field $mode 0", [];
61 } elsif ( $value =~ /^[0-9 ,]+$/ ) {
62 my @values = split(/\s*,\s*/, $value);
63 my $values_string = '?, 'x(scalar (@values)-1).'?';
64 return " ($field $mode ($values_string)) ", \@values;
65 } else {
66 my $mason_comp = ref($HTML::Mason::Commands::m) ? $HTML::Mason::Commands::m->current_comp() : undef;
67 my $mason_file = ref($mason_comp) ? $mason_comp->path : undef;
68 my ($package, $filename, $line) = caller;
69
70 warn "WARNING: $$: ������� ����� ������ integer ������� ��� $field ('$value'). ��� ����� ���� ���� �����, ���� ��� ����� ����� ������� ���� ������ �� ������ �����. called from '$package/$filename/$line' ".($mason_file ? "called from $mason_file" : '')."\n";
71 return ' FALSE ', [];
72 }
73 }
74
75 sub _generic_composite_filter {
76 my ($field, $condition, $value, $mode ) = @_;
77 unless ( $condition ) {
78 warn "Contenido Warning (_generic_composite_filter): ������� ����� ������!\n";
79 return ' FALSE ';
80 }
81 $condition = uc($condition) if $condition =~ /^[\w\s]+$/;
82 if ( $value eq NIL() ) {
83 return ' TRUE ';
84 } elsif ( defined($value) && !ref($value) ) {
85 if ( ( $mode eq 'text' && !$_TEXT_CONDITIONS{$condition} ) || ( $mode ne 'text' && !$_CONDITIONS{$condition} ) ) {
86 warn "Contenido Warning (_generic_composite_filter): ������� ����� ������ �����������, ������������ ������� \"$condition\"!\n";
87 return ' FALSE ';
88 }
89 return "$field $condition ?", $value;
90 } elsif ( ref($value) eq 'ARRAY' && scalar(@{ $value }) ) {
91 my $in = 'IN (';
92 my $i = 0;
93 unless ( $condition eq 'NOT' ) {
94 warn "Contenido Warning (_generic_composite_filter): ������� ����� ������, ������������ ������� \"$condition\"!\n";
95 return ' FALSE ';
96 }
97 foreach my $v ( @{ $value } ) {
98 $in .= '?';
99 $in .= ',' if $#{ $value } != $i;
100 $i++;
101 }
102 $in .= ')';
103 return "$field $condition $in", $value;
104 } else {
105 warn "Contenido Warning (_generic_composite_filter): ������� ������ ��������, ��������� ������ ��� ������ �� ������!\n ";
106 return ' FALSE ';
107 }
108 }
109
110 sub _composite_date_filter {
111 my ($field, $condition, $value ) = @_;
112 unless ( $condition ) {
113 warn "Contenido Warning (_composite_date_filter): ������� ����� ������!\n";
114 return ' FALSE ';
115 }
116 if ( $value eq NIL() ) {
117 return ' TRUE ';
118 } elsif ( defined($value) && !ref($value) ) {
119 unless ( $_DATE_CONDITIONS{$condition} ) {
120 warn "Contenido Warning (_composite_date_filter): ������� ����� ������ �����������, ������������ ������� \"$condition\"!\n";
121 return ' FALSE ';
122 }
123 if ( $value =~ /^\d+$/ ) {
124 my $date = Contenido::DateTime->new(epoch => $value)->strftime('%Y-%m-%d %H:%M:%S');
125 return " ($field $condition ?::TIMESTAMP) ", $date;
126 } elsif ( $value =~ /^\d{4}-\d{2}-\d{2}$/ || $value =~ /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(?:\:\d{2})?$/ ) {
127 return " ($field $condition ?::TIMESTAMP) ", $value;
128 } else {
129 warn "Contenido Warning (_composite_date_filter): ������� ������ ��������! ���������� ������ ������ \"YYYY-MM-DD[ HH24:MI[:SS]]\" ��� ���� � ������� unixtime\n";
130 return ' FALSE ';
131 }
132 } else {
133 warn "Contenido Warning (_composite_date_filter): ������� ������ ��������! ���������� ������ ������ \"YYYY-MM-DD[ HH24:MI[:SS]]\" ��� ���� � ������� unixtime\n";
134 return ' FALSE ';
135 }
136 }
137
138 sub _generic_null_filter {
139 my ($field, $param )= @_;
140 $param = uc($param);
141 if ( $param ne 'NULL' && $param ne 'NOT NULL' ) {
142 warn "Contenido Warning (_generic_null_filter): ������� ����� ��������, ��������� 'NULL' ��� 'NOT NULL'!\n";
143 return ' FALSE ';
144 }
145 return "$field IS $param";
146 }
147
148 sub _generic_date_filter {
149 my ($field, $param )= @_;
150 if ( defined($param) && !ref($param) ) {
151 if ( $param =~ /^\d+$/ ) {
152 my $date = Contenido::DateTime->new(epoch => $param)->strftime('%Y-%d-%m %H:%M');
153 return " ($field >= ?::TIMESTAMP AND $field < ?::TIMESTAMP+'1 min'::INTERVAL) ", [$date, $date];
154 } elsif ( $param =~ /^\d{4}-\d{2}-\d{2}$/ ) {
155 return " ($field >= ?::TIMESTAMP AND $field < ?::TIMESTAMP+'1 day'::INTERVAL) ", [$param, $param];
156 } elsif ( $param =~ /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(?:\:\d{2})?$/ ) {
157 return " ($field >= ?::TIMESTAMP AND $field < ?::TIMESTAMP+'1 min'::INTERVAL) ", [$param, $param];
158 } else {
159 warn "Contenido Warning (_generic_date_filter): ������� ������ ��������! ���������� ������ ������ \"YYYY-MM-DD[ HH24:MI[:SS]]\" ��� ���� � ������� unixtime\n";
160 return ' FALSE ';
161 }
162 } elsif ( !defined($param) ) {
163 return "$field IS NULL";
164 } else {
165 warn "Contenido Warning (_generic_date_filter): ������� ������ ��������! ���������� ������ ������ \"YYYY-MM-DD[ HH24:MI[:SS]]\" ��� ���� � ������� unixtime\n";
166 return ' FALSE ';
167 }
168 }
169
170 sub _generic_float_filter {
171 my ($field,$value,$negation)=@_;
172
173 my $mode = $negation ? 'NOT IN':'IN';
174
175 #undef <=> NULL
176 if ( !defined($value) ) {
177 $mode = $negation ? 'IS NOT' : 'IS';
178 return "$field $mode NULL", [];
179 } elsif (ref($value) eq 'ARRAY' and @$value) {
180 my $values_string = '?, 'x(scalar (@$value)-1).'?';
181 return " ($field $mode ($values_string)) ", $value;
182 } elsif (ref($value) eq 'ARRAY') {
183 # skip empty array
184 return ($negation ? ' TRUE ' : ' FALSE '), [];
185 } else {
186 return " ($field $mode (?)) ", [$value];
187 }
188 }
189
190 # raw flag for disable placeholders use... (need manual escaping before)... sometime need for partial indexes use with prepared statements
191 sub _generic_text_filter {
192 my ($field,$value,$negation,$raw)=@_;
193
194 my $mode = $negation ? 'NOT IN':'IN';
195
196 #undef <=> NULL
197 if ( !defined($value) ) {
198 $mode = $negation ? 'IS NOT' : 'IS';
199 return "$field $mode NULL", [];
200 } elsif (ref($value) eq 'ARRAY') {
201 if ($raw) {
202 my $values_string = "'".join ("','",@$value)."'";
203 return " ($field $mode ($values_string)) ", [];
204 } else {
205 my $values_string = '?, 'x(scalar (@$value)-1).'?';
206 return " ($field $mode ($values_string)) ", $value;
207 }
208 } else {
209 if ($raw) {
210 return " ($field $mode ('$value')) ", [];
211 } else {
212 return " ($field $mode (?)) ", [$value];
213 }
214 }
215 }
216
217 sub _generic_name_filter {
218 my ($field,$value,$negation,$opts)=@_;
219 $opts ||= {};
220
221 #like and ilike modes incompatible with [] in $opts{name}
222 if ($opts->{like}) {
223 my $mode = $negation ? "NOT" : "";
224 return " $field $mode LIKE ? ", [$value];
225 } elsif ($opts->{ilike}) {
226 my $mode = $negation ? "NOT" : "";
227 return " $field $mode ILIKE ? ", [$value];
228 } else {
229 return &SQL::Common::_generic_text_filter($field,$value,$negation,0);
230 }
231 }
232
233 sub _generic_intarray_filter {
234 my ($field, $value, $opts)=@_;
235
236 #undef <=> FALSE here!!!!
237 if (defined($value)) {
238 if (ref($value) ne 'ARRAY') {
239 if ($value =~ /^[\d ,]+$/) {
240 $value = [split(/\s*,\s*/, $value)];
241 } else {
242 warn "Contenido Warning: � ������ ��� _generic_int_array_filter ���� ���������� �������� ('$value'). �������� ������������.";
243 return [' FALSE '], [];
244 }
245 }
246
247 if (@$value) {
248 my $op = (ref($opts) eq 'HASH' and ($opts->{intersect} or $opts->{contains})) ? '@>' : '&&';
249 # old versions DBD::Pg is SO STUPID!!!!
250 # if ($DBD::Pg::VERSION<1.49) {
251 # my $value_string = '{'.join(',',@{$value}).'}';
252 # return [" ($field $op ?) "], [$value_string];
253 # } else {
254 # all versions before 2.0.0 also stupid
255 if ($DBD::Pg::VERSION=~/^1\./) {
256 my $ph_string = '?, 'x$#{$value}.'?';
257 return [" ($field $op ARRAY[$ph_string]::integer[]) "], $value;
258 } else {
259 return [" ($field $op ?::integer[]) "], [$value];
260 }
261 } else {
262 return [' FALSE '], [];
263 }
264 } else {
265 return [' FALSE '], [];
266 }
267 }
268
269 sub _get_limit {
270 my %opts=@_;
271 if (exists($opts{limit})) {
272 return ' LIMIT 1000' unless defined $opts{limit};
273 if ($opts{limit} !~ /\D/) {
274 return ' LIMIT '.$opts{limit};
275 } else {
276 warn "Contenido Warning: ������� ������ ������� ������� ($opts{limit})";
277 return ' LIMIT 1000';
278 }
279 } elsif ($opts{no_limit}) {
280 return undef;
281 } else {
282 return ' LIMIT 1000';
283 }
284 }
285
286 sub _get_offset {
287 my %opts=@_;
288 if ( exists($opts{offset})) {
289 return undef unless defined $opts{offset};
290 if ($opts{offset} !~ /\D/) {
291 return ' OFFSET '.$opts{offset};
292 } else {
293 my $mason_comp = ref($HTML::Mason::Commands::m) ? $HTML::Mason::Commands::m->current_comp() : undef;
294 my $mason_file = ref($mason_comp) ? $mason_comp->path : undef;
295
296 warn "ERROR: $$ ".__PACKAGE__." ".scalar(localtime())." ".($mason_file ? "called from $mason_file" : '')." ��� _get_offset ������� ����� �������� offset: '$opts{offset}'\n";
297 return undef;
298 }
299 }
300 }
301
302
303 1;