line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Minimalistic, yet fairly complete DBI Persister |
2
|
|
|
|
|
|
|
# Allow DB Persistence operations (insert(), load(), update(), delete(), |
3
|
|
|
|
|
|
|
# exists()) on a plain old hash (unblessed or blessed) without writing |
4
|
|
|
|
|
|
|
# classes, persistence code or SQL. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# Author: olli.hollmen@gmail.com |
7
|
|
|
|
|
|
|
# License: Perl License |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# StoredHash needs an OO instance of persister to function. |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# Because insert, update (the vals we want to pers.) are instance specific |
12
|
|
|
|
|
|
|
# Possibly return an object or bare hash from preparation of ins/upd/del |
13
|
|
|
|
|
|
|
# With |
14
|
|
|
|
|
|
|
# - query |
15
|
|
|
|
|
|
|
# - vals (to pass to exec) |
16
|
|
|
|
|
|
|
# - attr (needed ?) |
17
|
|
|
|
|
|
|
# - Assigned ID ? |
18
|
|
|
|
|
|
|
# Make this object w. meth execute() ???? getid() |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# TODO: Change/Add pkey => idattr @pkv => @idv |
21
|
|
|
|
|
|
|
# Support Mappings (before storage as separate op ?) |
22
|
|
|
|
|
|
|
package StoredHash; |
23
|
3
|
|
|
3
|
|
115434
|
use Scalar::Util ('reftype'); # |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
356
|
|
24
|
3
|
|
|
3
|
|
1239
|
use Data::Dumper; |
|
3
|
|
|
|
|
11939
|
|
|
3
|
|
|
|
|
13252
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
#use strict; |
27
|
|
|
|
|
|
|
#use warnings; |
28
|
|
|
|
|
|
|
our $VERSION = '0.029'; |
29
|
|
|
|
|
|
|
# Module extraction config |
30
|
|
|
|
|
|
|
our $mecfg = {}; |
31
|
|
|
|
|
|
|
# Instance attr (create access ...) |
32
|
|
|
|
|
|
|
# Allow 'attr' to act as attr filter |
33
|
|
|
|
|
|
|
my @opta = ('dbh', 'table','pkey','autoid','autoprobe','simu','errstr', |
34
|
|
|
|
|
|
|
'seqname','debug',); # |
35
|
|
|
|
|
|
|
# TODO: Support sequence for Oracle / Postgres |
36
|
|
|
|
|
|
|
# seq_emp.NEXTVAL |
37
|
|
|
|
|
|
|
my $bkmeta = { |
38
|
|
|
|
|
|
|
#'mysql' => {'iq' => "SELECT LAST_INSERT_ID()",}, |
39
|
|
|
|
|
|
|
#'Sybase' => {'iq' => "SELECT \@\@identity",}, |
40
|
|
|
|
|
|
|
'Oracle' => { |
41
|
|
|
|
|
|
|
#'iq' => "SELECT \@\@identity", |
42
|
|
|
|
|
|
|
'sv' => '%s.NEXTVAL',}, # AS adid SET NOCOUNT OFF |
43
|
|
|
|
|
|
|
# Postgres ??? |
44
|
|
|
|
|
|
|
}; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Create New instance of StoredHash Persister. |
47
|
|
|
|
|
|
|
# Options in %opt must have |
48
|
|
|
|
|
|
|
# - pkey/idattr - array (ref) to reflect the identifying attrtibute(s) of |
49
|
|
|
|
|
|
|
# entry (single attr for numeric ids, multiple for composite key) |
50
|
|
|
|
|
|
|
# Optional attributes |
51
|
|
|
|
|
|
|
# - dbh - DBI connection to database. Not passing 'dbh' makes |
52
|
|
|
|
|
|
|
# methods insert/update/load/delete return the SQL query only (as a string) |
53
|
|
|
|
|
|
|
sub new { |
54
|
3
|
|
|
3
|
1
|
1150
|
my ($class, %opt) = @_; |
55
|
3
|
|
|
|
|
8
|
my $self = {}; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Generate where by pkey OR use where |
58
|
|
|
|
|
|
|
#if ($opt{'where'}) {} |
59
|
|
|
|
|
|
|
# Moved for early bless |
60
|
3
|
|
|
|
|
8
|
bless($self, $class); |
61
|
|
|
|
|
|
|
# For Child loading / temp use |
62
|
3
|
50
|
|
|
|
13
|
if ($opt{'loose'}) {goto PASSPKEY;} |
|
0
|
|
|
|
|
0
|
|
63
|
3
|
50
|
|
|
|
12
|
if ($opt{'pkey'}) { |
|
0
|
|
|
|
|
0
|
|
64
|
3
|
|
|
|
|
14
|
$self->{'pkey'} = $opt{'pkey'}; |
65
|
|
|
|
|
|
|
# TODO: Do NOT cache WHERE id ... |
66
|
3
|
|
|
|
|
13
|
$self->{'where'} = whereid($self); # \%opt # join('AND', map({" $_ = ?";} pkeys(\%opt)); |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
else {die("Need pkey info");} |
69
|
3
|
|
|
|
|
36
|
PASSPKEY: |
70
|
|
|
|
|
|
|
# Validate seq. (Need additional params to note call for seq?) |
71
|
|
|
|
|
|
|
#if ($opt{'autoid'} eq 'seq') { |
72
|
|
|
|
|
|
|
# #$c{'seqcall'}; |
73
|
|
|
|
|
|
|
#} |
74
|
|
|
|
|
|
|
# Filter options to self |
75
|
|
|
|
|
|
|
@$self{@opta} = @opt{@opta}; |
76
|
|
|
|
|
|
|
|
77
|
3
|
|
|
|
|
11
|
return($self); |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
# Access error string that method may leave to object. |
80
|
|
|
|
|
|
|
# Notice that many methods throw exception (by die()) with |
81
|
|
|
|
|
|
|
# error message rather than leave it within object. |
82
|
|
|
|
|
|
|
sub errstr { |
83
|
0
|
|
|
0
|
1
|
0
|
my ($p, $v) = @_; |
84
|
0
|
0
|
|
|
|
0
|
if ($v) {$p->{'errstr'} = $v;} |
|
0
|
|
|
|
|
0
|
|
85
|
0
|
|
|
|
|
0
|
$p->{'errstr'}; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Internal method for executing query $q by filling placeholders with |
89
|
|
|
|
|
|
|
# values passed in @$vals. |
90
|
|
|
|
|
|
|
# Optional $rett (usually not passed) can force a special return type |
91
|
|
|
|
|
|
|
# Some supported return force tags: |
92
|
|
|
|
|
|
|
# - 'count' - number of entries counted with count(*) query |
93
|
|
|
|
|
|
|
# - 'sth' - return statement handle ($sth), which will be used outside. |
94
|
|
|
|
|
|
|
# - 'hash' - return a hash entry (first entry of resultset) |
95
|
|
|
|
|
|
|
# - 'aoh' - return array of hashes reflecting result set. |
96
|
|
|
|
|
|
|
# By default (no $rett) returns the ($ok)value from $sth->execute(). |
97
|
|
|
|
|
|
|
# Also by default statement statement handle gets properly closed |
98
|
|
|
|
|
|
|
# (If requested return type was $sth, the caller should take care of |
99
|
|
|
|
|
|
|
# calling $sth->finish() |
100
|
|
|
|
|
|
|
sub qexecute { |
101
|
0
|
|
|
0
|
0
|
0
|
my ($p, $q, $vals, $rett) = @_; |
102
|
0
|
|
|
|
|
0
|
my $dbh = $p->{'dbh'}; |
103
|
0
|
|
|
|
|
0
|
my $sth; # Keep here to have avail in callbacks below |
104
|
0
|
0
|
0
|
|
|
0
|
if (!$dbh || $p->{'simu'}) { # |
105
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Terse = 1; |
106
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Indent = 0; |
107
|
0
|
|
|
|
|
0
|
print("SQL($p->{'table'}): $q\nPlaceholder Vals:".Dumper($vals)."\n"); |
108
|
0
|
|
|
|
|
0
|
return(0); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
# Special Return value generators |
111
|
|
|
|
|
|
|
# These should also close the statement (if that is not returned) |
112
|
0
|
|
|
0
|
|
0
|
my $rets = { |
113
|
0
|
|
|
|
|
0
|
'count' => sub {my @a = $sth->fetchrow_array();$sth->finish();$a[0];}, |
|
0
|
|
|
|
|
0
|
|
114
|
0
|
|
|
0
|
|
0
|
'sth' => sub {return($sth);}, |
115
|
0
|
|
|
0
|
|
0
|
'hash' => sub {my $h = $sth->fetchrow_hashref();$sth->finish();$h;}, |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
116
|
0
|
|
|
0
|
|
0
|
'aoh' => sub {my $arr = $sth->fetchall_arrayref({});$sth->finish();$arr;}, |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
117
|
0
|
|
|
|
|
0
|
}; |
118
|
0
|
0
|
|
|
|
0
|
if (!$dbh) {$p->{'errstr'} = "No Connection !";return(0);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
119
|
0
|
0
|
|
|
|
0
|
if ($p->{'debug'}) {print("Full Q: $q\n");} |
|
0
|
|
|
|
|
0
|
|
120
|
|
|
|
|
|
|
# Prepare cached ? |
121
|
0
|
|
|
|
|
0
|
$sth = $dbh->prepare($q); |
122
|
0
|
0
|
|
|
|
0
|
if (!$sth) {die("Query ($q) Not prepared (".$dbh->errstr().")\n");} |
|
0
|
|
|
|
|
0
|
|
123
|
0
|
|
|
|
|
0
|
my $ok = $sth->execute(@$vals); |
124
|
0
|
0
|
|
|
|
0
|
if (!$ok) {die("Failed to execute ".$sth->errstr()."");} |
|
0
|
|
|
|
|
0
|
|
125
|
|
|
|
|
|
|
# Special return processing |
126
|
0
|
0
|
|
|
|
0
|
if (my $rcb = $rets->{$rett}) { |
127
|
|
|
|
|
|
|
#print("Special return by $rett ($rcb)\n"); |
128
|
0
|
|
|
|
|
0
|
return($rcb->()); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
# Done with statement |
131
|
|
|
|
|
|
|
DWS: |
132
|
0
|
|
|
|
|
0
|
$sth->finish(); |
133
|
0
|
|
|
|
|
0
|
return($ok); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
################################################### |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Store entry %$e (hash) inserting it as a new entry to a database. |
139
|
|
|
|
|
|
|
# Connection has been passed previously in construction of persister. |
140
|
|
|
|
|
|
|
# The table / schema to store to is either the one passed at |
141
|
|
|
|
|
|
|
# construction or derived from perl "blessing" of entry ($e). |
142
|
|
|
|
|
|
|
# Returns (ref to) an array of ID values for the entry that got stored (array |
143
|
|
|
|
|
|
|
# of one element for numeric primary key, multiple for composite key). |
144
|
|
|
|
|
|
|
sub insert { |
145
|
2
|
|
|
2
|
1
|
347
|
my ($p, $e) = @_; |
146
|
|
|
|
|
|
|
# No enforced internal validation |
147
|
2
|
50
|
|
|
|
4
|
eval {$p->validate();};if ($@) {$p->{'errstr'} = $@;return(1);} |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
8
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
148
|
2
|
50
|
|
|
|
11
|
if (reftype($e) ne 'HASH') {$p->errstr("Entry need to be HASH");return(2);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
149
|
|
|
|
|
|
|
# Possibly also test for references (ds branching ?) eliminating them too |
150
|
2
|
|
|
|
|
9
|
my @ea = sort (keys(%$e)); |
151
|
2
|
|
|
|
|
9
|
my @ev = @$e{@ea}; # map() |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# Sequence - Add sequenced ID allocation ??? |
154
|
|
|
|
|
|
|
# $p->{'seqname'} |
155
|
2
|
50
|
33
|
|
|
9
|
if ($p->{'autoid'} && ($p->{'autoid'} eq 'seq')) { |
156
|
0
|
|
|
|
|
0
|
my $bkt = 'Oracle'; |
157
|
0
|
|
|
|
|
0
|
my @pka = pkeys($p); |
158
|
0
|
0
|
|
|
|
0
|
if (@pka > 1) {die("Multiple pkeys for sequenced ID");} |
|
0
|
|
|
|
|
0
|
|
159
|
|
|
|
|
|
|
# Add Sequence id attibute AND sequence call (unshift to front ?) |
160
|
|
|
|
|
|
|
# |
161
|
0
|
|
|
|
|
0
|
push(@ea, @pka); # $p->{'pkey'}->[0] |
162
|
0
|
|
|
|
|
0
|
push(@ev, sprintf("$bkmeta->{$bkt}->{'sv'}", $p->{'seqname'}) ); # |
163
|
|
|
|
|
|
|
#DEBUG:print("FMT: $bkmeta->{$bkt}->{'sv'} / $p->{'seqname'}\n"); |
164
|
|
|
|
|
|
|
} |
165
|
6
|
|
|
|
|
24
|
my $qp = "INSERT INTO $p->{'table'} (".join(',',@ea).") ". |
166
|
2
|
|
|
|
|
12
|
"VALUES (".join(',', map({'?';} @ea)).")"; |
167
|
2
|
50
|
|
|
|
7
|
if (!$p->{'dbh'}) {return($qp);} |
|
2
|
|
|
|
|
17
|
|
168
|
0
|
|
|
|
|
0
|
my $okid = $p->qexecute($qp, \@ev); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Auto-id - either UTO_INC style or Sequence (works for seq. too ? |
171
|
0
|
0
|
|
|
|
0
|
if ($p->{'autoid'}) { |
172
|
0
|
|
|
|
|
0
|
my @pka = pkeys($p); |
173
|
0
|
0
|
|
|
|
0
|
if (@pka != 1) {die(scalar(@pka)." Keys for Autoid");} |
|
0
|
|
|
|
|
0
|
|
174
|
0
|
|
|
|
|
0
|
my $id = $p->fetchautoid(); |
175
|
|
|
|
|
|
|
#$e->{$pka[0]} = $id; |
176
|
0
|
|
|
|
|
0
|
return(($id)); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
# Seq ? |
179
|
|
|
|
|
|
|
#elsif () {} |
180
|
|
|
|
|
|
|
else { |
181
|
0
|
|
|
|
|
0
|
my @pka = pkeys($p); |
182
|
0
|
|
|
|
|
0
|
return(@$e{@pka}); |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Update an existing entry in the database with values in %$e (hash). |
187
|
|
|
|
|
|
|
# Provide protection for AUTO-ID (to not be changed) ? |
188
|
|
|
|
|
|
|
# For flexibility the $idvals may be hash or array (reference) with |
189
|
|
|
|
|
|
|
# hash containing (all) id keys and id values or alternatively array |
190
|
|
|
|
|
|
|
# containing id values IN THE SAME ORDER as keys were passed during |
191
|
|
|
|
|
|
|
# construction (with idattr/pkey parameter). |
192
|
|
|
|
|
|
|
sub update { |
193
|
2
|
|
|
2
|
1
|
11
|
my ($p, $e, $idvals) = @_; |
194
|
2
|
|
|
|
|
4
|
my @pka; # To be visible to closure |
195
|
|
|
|
|
|
|
# Extract ID Values from hash OR array |
196
|
0
|
|
|
0
|
|
0
|
my $idvgens = { |
197
|
|
|
|
|
|
|
'HASH' => sub {@$idvals{@pka};}, |
198
|
2
|
|
|
2
|
|
7
|
'ARRAY' => sub {return(@$idvals);}, |
199
|
|
|
|
|
|
|
#'' => sub {[$idvals];} |
200
|
2
|
|
|
|
|
19
|
}; |
201
|
|
|
|
|
|
|
# No mandatory (internal) validation ? |
202
|
|
|
|
|
|
|
#eval {$p->validate();};if ($@) {$p->{'errstr'} = $@;return(1);} |
203
|
2
|
|
|
|
|
5
|
@pka = pkeys($p); |
204
|
2
|
50
|
|
|
|
11
|
if (reftype($e) ne 'HASH') {$p->{'errstr'} = "Entry need to be hash";return(2);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
205
|
|
|
|
|
|
|
# Probe the type of $idvals |
206
|
2
|
|
|
|
|
6
|
my $idrt = reftype($idvals); |
207
|
2
|
50
|
|
|
|
7
|
if ($p->{'debug'}) {print("Got IDs:".Dumper($idvals)." as $idrt\n");} |
|
0
|
|
|
|
|
0
|
|
208
|
|
|
|
|
|
|
#my @idv; |
209
|
2
|
|
|
|
|
3
|
my @pkv; |
210
|
2
|
50
|
|
|
|
23
|
if (my $idg = $idvgens->{$idrt}) {@pkv = $idg->();} |
|
2
|
|
|
|
|
6
|
|
|
0
|
|
|
|
|
0
|
|
211
|
|
|
|
|
|
|
#if ($idrt ne 'HASH') {$p->{'errstr'} = "ID needs to be hash";return(3);} |
212
|
|
|
|
|
|
|
else {die("Need IDs as HASH or ARRAY (reference, got '$idrt')");} |
213
|
|
|
|
|
|
|
#my ($cnt_a, $cnt_v) = (scalar(@pka), scalar(@pkv)); |
214
|
2
|
50
|
|
|
|
8
|
if (@pkv != @pka) {die("Number of ID keys and values not matching for update");} |
|
0
|
|
|
|
|
0
|
|
215
|
2
|
|
|
|
|
9
|
my @ea = sort(keys(%$e)); |
216
|
|
|
|
|
|
|
#my @pkv = @$idh{@pka}; # $idvals, Does not work for hash |
217
|
|
|
|
|
|
|
|
218
|
2
|
50
|
|
|
|
9
|
if (my @badid = $p->invalidids(@pkv)) {$p->{'errstr'} = "Bad ID Values found (@badid)";return(4);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
219
|
2
|
|
|
|
|
16
|
my $widstr = whereid($p); |
220
|
|
|
|
|
|
|
# Persistent object type |
221
|
2
|
|
|
|
|
10
|
my $pot = $p->{'table'}; |
222
|
2
|
50
|
|
|
|
6
|
if (!$pot) {die("No table for update");} |
|
0
|
|
|
|
|
0
|
|
223
|
2
|
|
|
|
|
7
|
my $qp = "UPDATE $pot SET ".join(',', map({" $_ = ?";} @ea)). |
|
6
|
|
|
|
|
25
|
|
224
|
|
|
|
|
|
|
" WHERE $widstr"; |
225
|
2
|
50
|
|
|
|
7
|
if (!$p->{'dbh'}) {return($qp);} |
|
2
|
|
|
|
|
18
|
|
226
|
|
|
|
|
|
|
# Combine Entry attr values and primary key values |
227
|
0
|
|
|
|
|
0
|
my $allv = [@$e{@ea}, @pkv]; |
228
|
0
|
|
|
|
|
0
|
$p->qexecute($qp, $allv); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Delete an entry from database by passing $e as one of the following |
232
|
|
|
|
|
|
|
# - hash %$e - a hash containing (all) primary key(s) and their values. |
233
|
|
|
|
|
|
|
# - scalar $e - Entry ID for entry to be deleted |
234
|
|
|
|
|
|
|
# - array @$e - One or many primary key values for entry to be deleted |
235
|
|
|
|
|
|
|
# The recommended use is caae "array" as it is most versatile and most |
236
|
|
|
|
|
|
|
# consistent with other API methods. |
237
|
|
|
|
|
|
|
sub delete { |
238
|
2
|
|
|
2
|
1
|
10
|
my ($p, $e) = @_; |
239
|
|
|
|
|
|
|
#if (!ref($p->{'pkey'})) {die("PKA Not Known");} |
240
|
|
|
|
|
|
|
#eval {$p->validate();};if ($@) {$p->{'errstr'} = $@;return(1);} |
241
|
|
|
|
|
|
|
#my @pka = @{$p->{'pkey'}}; |
242
|
2
|
|
|
|
|
4
|
my @pka = pkeys($p); |
243
|
2
|
50
|
|
|
|
7
|
if (!$e) {die("Must have Identifier for delete()\n");} |
|
0
|
|
|
|
|
0
|
|
244
|
|
|
|
|
|
|
|
245
|
2
|
|
|
|
|
6
|
my $rt = reftype($e); |
246
|
2
|
|
|
|
|
7
|
my $pkc = $p->pkeycnt(); |
247
|
2
|
|
|
|
|
3
|
my @pkv; |
248
|
|
|
|
|
|
|
# $e Scalar, must have 1 pkey |
249
|
2
|
50
|
33
|
|
|
5368
|
if (!$rt && ($pkc == 1)) {@pkv = $e;} |
|
0
|
50
|
0
|
|
|
0
|
|
|
2
|
0
|
|
|
|
9
|
|
250
|
|
|
|
|
|
|
# Hash - extract primary keys |
251
|
0
|
|
|
|
|
0
|
elsif ($rt eq 'HASH') {@pkv = @$e{@pka};} |
252
|
|
|
|
|
|
|
# Array (of pk values) - check count matches |
253
|
0
|
|
|
|
|
0
|
elsif (($rt eq 'ARRAY') && ($pkc == scalar(@$e))) {@pkv = @$e;} |
254
|
|
|
|
|
|
|
else {die("No way to delete (without HASH or ARRAY for IDs)\n");} |
255
|
|
|
|
|
|
|
#NOTNEEDED:#my %pkh;@pkh{@pka} = @pkv; |
256
|
|
|
|
|
|
|
#my $wstr = join(' AND ', map({"$_ = ?";} @pka)); |
257
|
2
|
|
|
|
|
6
|
my $wstr = whereid($p); |
258
|
2
|
|
|
|
|
8
|
my $qp = "DELETE FROM $p->{'table'} WHERE $wstr"; |
259
|
2
|
50
|
|
|
|
9
|
if (!$p->{'dbh'}) {return($qp);} |
|
2
|
|
|
|
|
11
|
|
260
|
0
|
|
|
|
|
0
|
$p->qexecute($qp, \@pkv); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
#my $dbh = $p->{'dbh'}; |
263
|
|
|
|
|
|
|
#my $sth = $dbh->prepare($qp); |
264
|
|
|
|
|
|
|
#if (!$sth) {print("Not prepared\n");} |
265
|
|
|
|
|
|
|
#$sth->execute(@pkv); |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# Test if an entry exists in the DB table with ID values passed in @$ids (array). |
268
|
|
|
|
|
|
|
# Returns 1 (entry exists) or 0 (does not exist) under normal conditions. |
269
|
|
|
|
|
|
|
sub exists { |
270
|
2
|
|
|
2
|
1
|
12
|
my ($p, $ids) = @_; |
271
|
2
|
50
|
|
|
|
8
|
my $whereid = $p->{'where'} ? $p->{'where'} : whereid($p); |
272
|
2
|
|
|
|
|
6
|
my $qp = "SELECT COUNT(*) FROM $p->{'table'} WHERE $whereid"; |
273
|
2
|
50
|
|
|
|
9
|
if (!$p->{'dbh'}) {return($qp);} |
|
2
|
|
|
|
|
7
|
|
274
|
0
|
|
|
|
|
0
|
$p->qexecute($qp, $ids, 'count'); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# Load entry from DB table by its IDs passed in @$ids (array, |
278
|
|
|
|
|
|
|
# single id typical sequece autoid pkey, multiple for composite primary key). |
279
|
|
|
|
|
|
|
# Entry will be loaded from single table passed at construction |
280
|
|
|
|
|
|
|
# (never as result of join from multiple tables). |
281
|
|
|
|
|
|
|
# Return entry as a hash (ref). |
282
|
|
|
|
|
|
|
sub load { |
283
|
2
|
|
|
2
|
1
|
11
|
my ($p, $ids) = @_; |
284
|
2
|
50
|
|
|
|
6
|
my $whereid = $p->{'where'} ? $p->{'where'} : whereid($p); |
285
|
2
|
|
|
|
|
7
|
my $qp = "SELECT * FROM $p->{'table'} WHERE $whereid"; |
286
|
2
|
50
|
|
|
|
7
|
if (!$p->{'dbh'}) {return($qp);} |
|
2
|
|
|
|
|
6
|
|
287
|
0
|
|
|
|
|
0
|
$p->qexecute($qp, $ids, 'hash'); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Load a set of Entries from persistent storage. |
291
|
|
|
|
|
|
|
# Optionally provide simple "where filter hash" ($h), whose key-value criteria |
292
|
|
|
|
|
|
|
# is ANDed together to form the filter. |
293
|
|
|
|
|
|
|
# Return set / collection of entries as array of hashes. |
294
|
|
|
|
|
|
|
sub loadset { |
295
|
0
|
|
|
0
|
1
|
0
|
my ($p, $h, $sort) = @_; # filter, sortby |
296
|
0
|
|
|
|
|
0
|
my $w = ''; |
297
|
|
|
|
|
|
|
# if (@_ = 2 && ref($_[1]) eq 'HASH') {} |
298
|
0
|
0
|
|
|
|
0
|
if ($h) { |
299
|
0
|
|
|
|
|
0
|
my $wf = wherefilter($h); |
300
|
0
|
0
|
|
|
|
0
|
if (!$wf) {die("Empty Filter !");} |
|
0
|
|
|
|
|
0
|
|
301
|
0
|
|
|
|
|
0
|
$w = " WHERE $wf"; |
302
|
|
|
|
|
|
|
} |
303
|
0
|
0
|
|
|
|
0
|
if ($p->{'debug'}) {print("Loading set by '$w'\n");} |
|
0
|
|
|
|
|
0
|
|
304
|
0
|
|
|
|
|
0
|
my $qp = "SELECT * FROM $p->{'table'} $w"; |
305
|
0
|
|
|
|
|
0
|
$p->qexecute($qp, undef, 'aoh'); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# Sample Column names from (current) DB table. |
309
|
|
|
|
|
|
|
# Return (ref to) array with field names in it. |
310
|
|
|
|
|
|
|
sub cols { |
311
|
0
|
|
|
0
|
1
|
0
|
my ($p) = @_; |
312
|
0
|
|
|
|
|
0
|
my $qp = "SELECT * FROM $p->{'table'} WHERE 1 = 0"; |
313
|
0
|
|
|
|
|
0
|
my $sth = $p->qexecute($qp, undef, 'sth'); |
314
|
0
|
|
|
|
|
0
|
my $cols = $sth->{'NAME'}; |
315
|
0
|
0
|
|
|
|
0
|
if (@_ == 1) {$sth->finish();return($cols);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
316
|
|
|
|
|
|
|
#elsif (@_ == 2) {$rett = $_[1];}; |
317
|
|
|
|
|
|
|
#if ($rett ne 'meta') {return(undef);} |
318
|
0
|
|
|
|
|
0
|
return(undef); |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# TODO: Load "tree" of entries rooted at an entry / entries (?) |
322
|
|
|
|
|
|
|
# Returns a set (array) of entries or single (root entry if |
323
|
|
|
|
|
|
|
# option $c{'fsingle'} - force single - is set. |
324
|
|
|
|
|
|
|
sub loadtree { |
325
|
0
|
|
|
0
|
0
|
0
|
my ($p, %c) = @_; |
326
|
0
|
|
|
|
|
0
|
my $chts = $c{'ctypes'}; |
327
|
0
|
|
|
|
|
0
|
my $w = $c{'w'}; |
328
|
0
|
|
|
|
|
0
|
my $fsingle = $c{'fsingle'}; # singleroot, uniroot |
329
|
0
|
|
|
|
|
0
|
my $arr = loadset($p, $w); |
330
|
0
|
|
|
|
|
0
|
for my $e (@$arr) {my $err = loadchildern($p, $e, %c);} |
|
0
|
|
|
|
|
0
|
|
331
|
|
|
|
|
|
|
# Choose return type |
332
|
0
|
0
|
|
|
|
0
|
if ($fsingle) {return($arr->[0]);} |
|
0
|
|
|
|
|
0
|
|
333
|
0
|
|
|
|
|
0
|
return($arr); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# TODO: Load Instances of child object types for entry. |
337
|
|
|
|
|
|
|
# Child types are defined in 'ctypes' array(ref) in options. |
338
|
|
|
|
|
|
|
# Array 'ctypes' may be one of the following |
339
|
|
|
|
|
|
|
# - Plain child type names (array of scalars), the rest is guessed |
340
|
|
|
|
|
|
|
# - Array of child type definition hashes with hashes defining following: |
341
|
|
|
|
|
|
|
# - table - The table / objectspace of child type |
342
|
|
|
|
|
|
|
# - parkey - Parent id field in child ("foreign key" field in rel DBs) |
343
|
|
|
|
|
|
|
# - memname - Mamber name to place the child collection into in parent entry |
344
|
|
|
|
|
|
|
# - Array of arrays with inner arrays containing 'table','parkey','memname' in |
345
|
|
|
|
|
|
|
# that order(!), (see above for meanings) |
346
|
|
|
|
|
|
|
# Return 0 for no errors |
347
|
|
|
|
|
|
|
sub loadchildren { |
348
|
0
|
|
|
0
|
0
|
0
|
my ($p, $e, %c) = @_; |
349
|
0
|
|
|
|
|
0
|
my $chts = $c{'ctypes'}; |
350
|
0
|
0
|
|
|
|
0
|
if (!$chts) {die("No Child types indicated");} |
|
0
|
|
|
|
|
0
|
|
351
|
0
|
0
|
|
|
|
0
|
if (ref($chts) ne 'ARRAY') {die("Child types not ARRAY");} |
|
0
|
|
|
|
|
0
|
|
352
|
0
|
|
|
|
|
0
|
my @ids = pkeyvals($p, $e); |
353
|
0
|
0
|
|
|
|
0
|
if (@ids > 1) {die("Loading not supported for composite keys");} |
|
0
|
|
|
|
|
0
|
|
354
|
0
|
|
|
|
|
0
|
my $dbh = $p->{'dbh'}; |
355
|
0
|
|
|
|
|
0
|
my $debug = $p->{'debug'}; |
356
|
0
|
|
|
|
|
0
|
for (@$chts) { |
357
|
|
|
|
|
|
|
#my $ct = $_; |
358
|
0
|
|
|
|
|
0
|
my $cfilter; |
359
|
|
|
|
|
|
|
# Use or create a complete hash ? |
360
|
0
|
|
|
|
|
0
|
my $cinfo = makecinfo($p, $_); |
361
|
0
|
0
|
|
|
|
0
|
if ($debug) {print(Dumper($cinfo));} |
|
0
|
|
|
|
|
0
|
|
362
|
|
|
|
|
|
|
# Load type by created filter |
363
|
0
|
|
|
|
|
0
|
my ($ct, $park, $memn) = @$cinfo{'table','parkey','memname',}; |
364
|
0
|
0
|
|
|
|
0
|
if (!$park) {} |
365
|
|
|
|
|
|
|
# Create where by parkey info |
366
|
|
|
|
|
|
|
#$cfilter = {$park => $ids[0]}; # What is par key - assume same as parent |
367
|
0
|
0
|
|
|
|
0
|
if (@$park != @ids) {die("Par and child key counts mismatch");} |
|
0
|
|
|
|
|
0
|
|
368
|
0
|
|
|
|
|
0
|
@$cfilter{@$park} = @ids; |
369
|
|
|
|
|
|
|
#my $cfilter = |
370
|
|
|
|
|
|
|
# Take a shortcut by not providing pkey |
371
|
0
|
|
|
|
|
0
|
my $shc = StoredHash->new('table' => $ct, 'pkey' => [], |
372
|
|
|
|
|
|
|
'dbh' => $dbh, 'loose' => 1, 'debug' => $debug); |
373
|
0
|
|
|
|
|
0
|
my $carr = $shc->loadset($cfilter); |
374
|
0
|
0
|
0
|
|
|
0
|
if (!$carr || !@$carr) {next;} |
|
0
|
|
|
|
|
0
|
|
375
|
|
|
|
|
|
|
#if ($debug) {print("Got Children".Dumper($arr));} |
376
|
0
|
|
|
|
|
0
|
$e->{$memn} = $carr; |
377
|
|
|
|
|
|
|
# Blessing |
378
|
0
|
0
|
|
|
|
0
|
if (my $bto = $cinfo->{'blessto'}) {map({bless($_, $bto);} @$carr);} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
379
|
|
|
|
|
|
|
# Circular Ref from child to parent ? |
380
|
|
|
|
|
|
|
#if (my $pla = $cinfo->{'parlinkattr'}) {map({$_->{$pla} = $e;} @$carr);} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
# Autobless Children ? |
383
|
0
|
|
|
|
|
0
|
return(0); |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
# Internal method for using or making up Child relationship information |
386
|
|
|
|
|
|
|
# for loading related entities. |
387
|
|
|
|
|
|
|
sub makecinfo { |
388
|
0
|
|
|
0
|
0
|
0
|
my ($p, $cv) = @_; |
389
|
|
|
|
|
|
|
# Support array with: 'table','parkey','memname' |
390
|
0
|
0
|
|
|
|
0
|
if (ref($cv) eq 'ARRAY') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
my $cinfo; |
392
|
0
|
0
|
|
|
|
0
|
if (@$cv != 3) {die("Need table, parkey, memname in array");} |
|
0
|
|
|
|
|
0
|
|
393
|
0
|
|
|
|
|
0
|
@$cinfo{'table','parkey','memname'} = @$cv; |
394
|
0
|
|
|
|
|
0
|
return($cinfo); |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
# Assume all is there (could validate and provide missing) |
397
|
0
|
|
|
|
|
0
|
elsif (ref($cv) eq 'HASH') { |
398
|
0
|
|
|
|
|
0
|
my @a = ('table','parkey','memname'); |
399
|
|
|
|
|
|
|
# Try guess parkey ? |
400
|
0
|
0
|
|
|
|
0
|
if (!$cv->{'parkey'}) {$cv->{'parkey'} = [pkeys($p)];} |
|
0
|
|
|
|
|
0
|
|
401
|
0
|
0
|
|
|
|
0
|
for (@a) {if (!$cv->{$_}) {die("Missing '$_' in cinfo");}} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
402
|
0
|
|
|
|
|
0
|
return($cv); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
elsif (ref($cv) ne '') {die("child type Not scalar (or hash)");} |
405
|
|
|
|
|
|
|
################## Make up |
406
|
0
|
|
|
|
|
0
|
my $ctab = $cv; |
407
|
0
|
|
|
|
|
0
|
my $memname = $ctab; # Default memname to child type name (Plus 's') ? |
408
|
|
|
|
|
|
|
# Guess by parent |
409
|
0
|
|
|
|
|
0
|
my $parkey = [pkeys($p)]; |
410
|
0
|
|
|
|
|
0
|
my $cinfo = {'table' => $ctab, 'parkey' => $parkey, 'memname' => $ctab,}; |
411
|
0
|
|
|
|
|
0
|
return($cinfo); |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
################################################################### |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# Internal Persister validator for the absolutely mandatory properties of |
416
|
|
|
|
|
|
|
# persister object itself. |
417
|
|
|
|
|
|
|
# Doesn't not validate entry |
418
|
|
|
|
|
|
|
sub validate { |
419
|
2
|
|
|
2
|
0
|
3
|
my ($p) = @_; |
420
|
2
|
50
|
|
|
|
11
|
if (ref($p->{'pkey'}) ne 'ARRAY') {die("PK Attributes Not Known\n");} |
|
0
|
|
|
|
|
0
|
|
421
|
|
|
|
|
|
|
# Allow table to come from blessing (so NOT required) |
422
|
|
|
|
|
|
|
#if (!$p->{'table'}) {die("No Table\n");} |
423
|
2
|
50
|
|
|
|
18
|
if ($p->{'simu'}) {return;} |
|
0
|
|
|
|
|
0
|
|
424
|
|
|
|
|
|
|
# Do NOT Require conenction |
425
|
|
|
|
|
|
|
#if (!ref($p->{'dbh'})) {die("NO dbh to act on\n");} # ne 'DBI' |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# Internal method for returning array of id keys (Real array, not ref). |
430
|
|
|
|
|
|
|
sub pkeys { |
431
|
13
|
|
|
13
|
0
|
20
|
my ($p) = @_; |
432
|
13
|
|
|
|
|
39
|
my $prt = reftype($p); |
433
|
13
|
50
|
|
|
|
38
|
if ($prt ne 'HASH') { |
434
|
0
|
|
|
|
|
0
|
$|=1; |
435
|
0
|
|
|
|
|
0
|
print STDERR Dumper([caller(1)]); |
436
|
0
|
|
|
|
|
0
|
die("StoredHash Not a HASH (is '$p'/'$prt')"); |
437
|
|
|
|
|
|
|
} |
438
|
13
|
50
|
|
|
|
40
|
if (ref($p->{'pkey'}) ne 'ARRAY') {die("Primary keys not in an array");} |
|
0
|
|
|
|
|
0
|
|
439
|
|
|
|
|
|
|
#return($p->{'pkey'}); |
440
|
13
|
|
|
|
|
17
|
return(@{$p->{'pkey'}}); |
|
13
|
|
|
|
|
48
|
|
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# Return Primary key values (as real array) from hash %$e passed as parameter. |
444
|
|
|
|
|
|
|
# undef values are produced for non-existing keys. |
445
|
|
|
|
|
|
|
# Mostly used for internal operations (and maybe debugging). |
446
|
|
|
|
|
|
|
sub pkeyvals { |
447
|
0
|
|
|
0
|
1
|
0
|
my ($p, $e) = @_; |
448
|
0
|
|
|
|
|
0
|
my @pkeys = pkeys($p); |
449
|
0
|
|
|
|
|
0
|
@$e{@pkeys}; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# TODO: Implement pulling last id from sequence |
453
|
|
|
|
|
|
|
sub fetchautoid { |
454
|
0
|
|
|
0
|
0
|
0
|
my ($p) = @_; |
455
|
0
|
|
|
|
|
0
|
my $dbh; |
456
|
|
|
|
|
|
|
#$dbh->{'Driver'}; # Need to test ? |
457
|
|
|
|
|
|
|
#DEV:print("AUTOID FETCH TO BE IMPLEMENTED\n");return(69); |
458
|
0
|
|
|
|
|
0
|
my $pot = $p->{'table'}; |
459
|
0
|
0
|
|
|
|
0
|
if (!$pot) {die("No table for fetching auto-ID");} |
|
0
|
|
|
|
|
0
|
|
460
|
0
|
0
|
|
|
|
0
|
if (!($dbh = $p->{'dbh'})) {die("No Connection for fetching ID");} |
|
0
|
|
|
|
|
0
|
|
461
|
0
|
|
|
|
|
0
|
$dbh->last_insert_id(undef, undef, $pot, undef); |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub pkeycnt { |
465
|
2
|
|
|
2
|
0
|
4
|
my ($p) = @_; |
466
|
|
|
|
|
|
|
#if (ref($p->{'pkey'}) ne 'ARRAY') {die("Primary keys not in an array");} |
467
|
|
|
|
|
|
|
#scalar(@{$p->{'pkey'}}); |
468
|
2
|
|
|
|
|
5
|
my @pkeys = pkeys($p); |
469
|
2
|
|
|
|
|
6
|
scalar(@pkeys); |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# Internal method for checking for empty or undefined ID values. |
473
|
|
|
|
|
|
|
# In all reasonable databases and apps these are not valid values. |
474
|
|
|
|
|
|
|
sub invalidids { |
475
|
2
|
|
|
2
|
0
|
4
|
my ($p, @idv) = @_; |
476
|
2
|
50
|
|
|
|
5
|
my @badid = grep({!defined($_) || $_ eq '';} @idv); |
|
3
|
|
|
|
|
23
|
|
477
|
2
|
|
|
|
|
9
|
return(@badid); |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
# Generate SQL WHERE Clause for UPDATE based on primary keys of current type. |
480
|
|
|
|
|
|
|
# Return WHERE clause with id-attribute(s) and placeholder(s) (idkey = ?, ...), without the WHERE keyword. |
481
|
|
|
|
|
|
|
sub whereid { |
482
|
7
|
|
|
7
|
1
|
13
|
my ($p) = @_; |
483
|
|
|
|
|
|
|
# # Allow IDs to be hash OR array ?? Not because hash would req. to store order |
484
|
7
|
|
|
|
|
22
|
my @pka = pkeys($p); |
485
|
7
|
50
|
|
|
|
21
|
if (@pka < 1) {die("No Pkeys to create where ID clause");} |
|
0
|
|
|
|
|
0
|
|
486
|
|
|
|
|
|
|
# my $wstr = |
487
|
7
|
|
|
|
|
13
|
return join(' AND ', map({"$_ = ?";} @pka)); |
|
10
|
|
|
|
|
43
|
|
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub sqlvalesc { |
491
|
0
|
|
|
0
|
0
|
0
|
my ($v) = @_; |
492
|
0
|
|
|
|
|
0
|
$v =~ s/'/\\'/g; |
493
|
0
|
|
|
|
|
0
|
$v; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# TODO: Create list for WHERE IN Clause based on some assumptions |
497
|
|
|
|
|
|
|
sub invalues { |
498
|
0
|
|
|
0
|
0
|
0
|
my ($vals) = @_; |
499
|
|
|
|
|
|
|
# Assume array ref validated outside |
500
|
0
|
0
|
|
|
|
0
|
if (ref($vals) eq 'ARRAY') {die("Not an array for invals");} |
|
0
|
|
|
|
|
0
|
|
501
|
|
|
|
|
|
|
# Escape within Quotes ? |
502
|
0
|
|
|
|
|
0
|
join(',', map({ |
503
|
0
|
0
|
|
|
|
0
|
if (/^\d+$/) {$_;} |
|
0
|
|
|
|
|
0
|
|
504
|
|
|
|
|
|
|
else { |
505
|
0
|
|
|
|
|
0
|
my $v = sqlvalesc($_); |
506
|
0
|
|
|
|
|
0
|
"'$v'"; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
} @$vals)); |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub rangefilter { |
512
|
0
|
|
|
0
|
0
|
0
|
my ($attr, $v) = @_; |
513
|
|
|
|
|
|
|
# Or just even and sort, grab 2 at the time ? |
514
|
0
|
0
|
|
|
|
0
|
if (@$v != 2) {die("Range cannot be formed");} |
|
0
|
|
|
|
|
0
|
|
515
|
|
|
|
|
|
|
# Auto-arrange ??? |
516
|
|
|
|
|
|
|
#if ($v->[1] < $v->[0]) {$v = [$v->[1],$v->[0]];} |
517
|
|
|
|
|
|
|
# Detect need to escape (time vs. number) |
518
|
|
|
|
|
|
|
#"($attr >= $v->[0]) AND ($attr <= $v->[0])"; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
# Generate simple WHERE filter by hash %$e. The keys are assumed to be attributes |
522
|
|
|
|
|
|
|
# of DB and values are embedded as values into SQL (as opposed to using placeholers). |
523
|
|
|
|
|
|
|
# To be perfect in escaping per attribute type info would be needed. |
524
|
|
|
|
|
|
|
# For now we do best effort heuristics (attr val \d+ is assumed |
525
|
|
|
|
|
|
|
# to be a numeric field in SQL, however 000002345 could actually |
526
|
|
|
|
|
|
|
# be content of a char/text/varchar field). |
527
|
|
|
|
|
|
|
# Return WHERE filter clause without WHERE keyword. |
528
|
|
|
|
|
|
|
sub wherefilter { |
529
|
0
|
|
|
0
|
1
|
0
|
my ($e, %c) = @_; |
530
|
0
|
|
|
|
|
0
|
my $w = ''; |
531
|
0
|
|
|
|
|
0
|
my $fop = ' AND '; |
532
|
|
|
|
|
|
|
#my $rnga = $c{'rnga'}; # Range attributes |
533
|
0
|
0
|
|
|
|
0
|
if (ref($e) ne 'HASH') {die("No hash for filter generation");} |
|
0
|
|
|
|
|
0
|
|
534
|
0
|
|
|
|
|
0
|
my @keys = sort keys(%$e); |
535
|
0
|
|
|
|
|
0
|
my @qc; |
536
|
|
|
|
|
|
|
# Assume hard values, treat everything as string (?) |
537
|
|
|
|
|
|
|
# TODO: forcestr ? |
538
|
0
|
|
|
|
|
0
|
@qc = map({ |
539
|
0
|
|
|
|
|
0
|
my $v = $e->{$_}; |
540
|
|
|
|
|
|
|
#my $rv = ref($v); |
541
|
|
|
|
|
|
|
#if ($rnga->{$_} && ($rv eq 'ARRAY') && (@$v == 2)) {rangefilter($_, $v);} |
542
|
0
|
0
|
|
|
|
0
|
if (ref($v) eq 'ARRAY') {" $_ IN (".invalues($v).") ";} |
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
543
|
|
|
|
|
|
|
# SQL Wildcard |
544
|
0
|
|
|
|
|
0
|
elsif ($v =~ /%/) {"$_ LIKE '$v'";} |
545
|
|
|
|
|
|
|
# Detect numeric (likely numeric, not perfect) |
546
|
0
|
|
|
|
|
0
|
elsif ($v =~ /^\d+$/) {"$_ = $v";} |
547
|
|
|
|
|
|
|
# Assume string |
548
|
|
|
|
|
|
|
else {"$_ = '".sqlvalesc($v)."'";} |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
} @keys); |
551
|
0
|
|
|
|
|
0
|
return(join($fop, @qc)); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# Internal: Serialize all values (singles,multi) from a hash to an array |
555
|
|
|
|
|
|
|
# based on sorted key order. Multi-valued keys (with value being array reference) |
556
|
|
|
|
|
|
|
# add multiple items. |
557
|
|
|
|
|
|
|
sub allentvals { |
558
|
2
|
|
|
2
|
0
|
19
|
my ($h) = @_; |
559
|
0
|
|
|
|
|
0
|
map({ |
560
|
2
|
50
|
|
|
|
14
|
if (ref($h->{$_}) eq 'HASH') {();} |
|
6
|
50
|
|
|
|
24
|
|
|
0
|
|
|
|
|
0
|
|
561
|
0
|
|
|
|
|
0
|
elsif (ref($h->{$_}) eq 'ARRAY') {@{$h->{$_}};} |
|
6
|
|
|
|
|
19
|
|
562
|
|
|
|
|
|
|
else {($h->{$_});} |
563
|
|
|
|
|
|
|
} sort(keys(%$h))); |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
1; |