|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 StoredHash - Minimalistic, yet fairly complete DBI Persister with a definite NoSQL feel to it  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SYNOPSIS  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    use StoredHash;  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    use DBI;  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    use Data::Dumper;  | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $dbh = DBI->connect("dbi:SQLite:dbname=/tmp/zoo.db");  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Lightweight demonstration of StoredHash in action (with SQLite)  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $dbh->do("CREATE TABLE animals (speciesid INTEGER NOT NULL PRIMARY KEY, name CHAR(16), limbcnt INTEGER, family CHAR(16))");  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $shp = StoredHash->new('table' => 'animals', 'pkey' => ['speciesid'],  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       'autoid' => 1, 'dbh' => $dbh, 'debug' => 0);  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Hash object to be stored  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $monkey = {'name' => 'Common Monkey', 'limbcnt' => 5, 'family' => 'mammal',};  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Happens to return numeric id (because of auto-increment pkey / autoid)  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $spid = $shp->insert($monkey);  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Created by: id=$spid\n");  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Load entry  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $ent = $shp->load([$spid]);  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Fetched (by $spid): ".Dumper($ent)."\n");  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Get: {'name' => 'Common Monkey', 'speciesid' => 469, 'limbcnt' => 5,'family' => 'mammal',}  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Fix error in entry (don't count tail to be limb)  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $ent->{'limbcnt'} = 4;  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Update (with some redundant attributes that do not change)  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Update $ent->{'speciesid'}\n");  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $shp->update($ent, [$ent->{'speciesid'}]);  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Could reduce / optimize change to bare minimum:  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my %change = ('limbcnt' => 4);  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Reduce property value on $spid\n");  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $shp->update(\%change, [$spid]);  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Later ... (species dies extinct ?)  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #$shp->delete([$spid]);  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Test if we need to insert / update (based on presence in DB)  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $id = 5987;  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $invals = {'name' => 'Crow', 'limbcnt' => 4, 'family' => 'birds'};  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Test Presence of Animal '$id'\n");  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if ($shp->exists([$id])) {$shp->update($invals, [$id]);}  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {$shp->insert($invals);}  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    ##### Easy loading of sets / collections  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Load all the animals  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $animarr = $shp->loadset();  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("All Animals: ".Dumper($animarr)."\n");  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Load only mammals (by filter)  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $mammarr = $shp->loadset({'family' => 'mammal'});  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    print("Mammals: ".Dumper($mammarr)."\n");  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Allow DB Persistence operations (insert(), load(), update(), delete(),  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 exists()) on a plain old hash (unblessed or blessed) without writing  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 classes, persistence code or SQL.  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Optionally StoredHash allows your classes to inherit peristence capability from StoredHash allowing your objects  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to call StoredHash persistence methoda via object directly.  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 GENERAL INFO ON StoredHash PERSISTENCE  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * Connection is stored in persister. Thus there is no need to pass it as parameter to persister methods.  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * Composite keys are supported by StoredHash. Because of this id values are passed in array. Id values must  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 be ordered the same as their attribute names suring construction (as passed in 'pkey' construction parameter).  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * Some persistence methods support 'attrs' parameter. This means "partial attributes" or "only these attributes" whatever the direction is  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 persistence operation is. Examples: load(): load only these attributes, update(): update only these attributes, etc.  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * StoredHash is not validating the hash keys / attribute (or 'attrs' parameter above) against these attributes  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 actually existing in DB schema. Caller of persistence methods is responsible validating the "fit" of hash to a schema.  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Above principles are consistent across persistence methods. These details will be not repeated in method documentation.  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Maintain a good NoSQL feel with your SQL database.  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ... a good nonrelation relationship  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Author: olli.hollmen@gmail.com  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # License: Perl License  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # StoredHash needs an OO instance of persister to function.  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Because insert, update (the vals we want to pers.) are instance specific  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Possibly return an object or bare hash from preparation of ins/upd/del  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # With   | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # - query  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # - vals (to pass to exec)  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # - attr (needed ?)  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # - Assigned ID ?  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  Make this object w. meth execute() ???? getid()  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # http://www.nntp.perl.org/group/perl.dbi.dev/2010/03/msg5887.html  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ANSI X3.135 and ISO/IEC 9075  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ftp://sqlstandards.org/SC32/SQL_Registry/   | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Change/Add pkey => idattr @pkv => @idv  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Support Mappings (before storage as separate op ?)  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package StoredHash;  | 
| 
107
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
 
 | 
93454
 | 
 use Scalar::Util ('reftype'); #   | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
458
 | 
    | 
| 
108
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
 
 | 
1116
 | 
 use Data::Dumper;  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12196
 | 
    | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
315
 | 
    | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $hardval = 0; # 1= Call $dbh->do() 2=Return query  | 
| 
110
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
 
 | 
41
 | 
 use strict;  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
153
 | 
    | 
| 
111
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
 
 | 
18
 | 
 use warnings;  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25478
 | 
    | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.031';  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Module extraction config  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #our $mecfg = {};  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Instance attributes (create accessors ?)  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Allow 'attr' to act as attr filter  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my @opta = ('dbh', 'table','pkey','autoid','autoprobe','simu','errstr',  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    'seqname','debug',); #   | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Support sequence for Oracle / Postgres  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # seq_emp.NEXTVAL  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $bkmeta = {  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #'mysql'  => {'iq' => "SELECT LAST_INSERT_ID()",},  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #'Sybase' => {'iq' => "SELECT \@\@identity",},  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    'Oracle' => {  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #'iq' => "SELECT \@\@identity",  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       'sv' => '%s.NEXTVAL',}, # AS adid SET NOCOUNT OFF  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Postgres ???  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Tentative class-level / static structures:  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # persister cache: $shpcache = {}; # Keyed by 'table'  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # query cached: $qcache = {}; # Allow prepared and params ? plain K-V or HoH ?  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 METHODS  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp = StoredHash->new(%opts);  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Create new instance of StoredHash Persister.  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Keyword parameters in %opts:  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * 'pkey' - array (ref) to reflect the identifying attrtibute(s) of  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 entry (e.g. single attr for numeric sequential ids, multiple for composite key)  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * 'dbh' - DBI connection to database (optional). Not passing 'dbh' makes  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 methods insert/update/load/delete return the SQL query only (as a string)  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
153
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
  
1
  
 | 
1851
 | 
    my ($class, %opt) = @_;  | 
| 
154
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    my $self = {};  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Generate where by pkey OR use where  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if ($opt{'where'}) {}  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Moved for early bless  | 
| 
159
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    bless($self, $class);  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # For Child loading / temp use  | 
| 
161
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
426
 | 
    if ($opt{'loose'}) {goto PASSPKEY;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
162
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    if ($opt{'pkey'}) {  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
163
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
       $self->{'pkey'} = $opt{'pkey'};  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # TODO: Do NOT cache WHERE id ...  | 
| 
165
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
       $self->{'where'} = whereid($self); # \%opt # join('AND', map({" $_ = ?";} pkeys(\%opt));  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {die("Need pkey info");}  | 
| 
168
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
    PASSPKEY:  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Validate seq. (Need additional params to note call for seq?)  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if ($opt{'autoid'} eq 'seq') {  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #   #$c{'seqcall'};  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #}  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Filter options to self  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    @$self{@opta} = @opt{@opta};  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
176
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    return($self);  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->errstr($v)  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Access error string that method may leave to object.  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Notice that many methods throw exception (by die()) with  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 error message rather than leave it within object.  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub errstr {  | 
| 
187
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
    my ($p, $v) = @_;  | 
| 
188
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($v) {$p->{'errstr'} = $v;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
189
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->{'errstr'};  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal method for executing query $q by filling placeholders with  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # values passed in @$vals.  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Optional $rett (usually not passed) can force a special return type  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Some supported return force tags:  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * 'count' - number of entries counted with count(*) query  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * 'sth' - return statement handle ($sth), which will be used outside.  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * 'hash' - return a hash entry (first entry of resultset)  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * 'aoh'  - return array of hashes reflecting result set.  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # By default (no $rett) returns the ($ok)value from $sth->execute().  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Also by default statement statement handle gets properly closed  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # (If requested return type was $sth, the caller should take care of  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # calling $sth->finish()  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub qexecute {  | 
| 
205
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($p, $q, $vals, $rett) = @_;  | 
| 
206
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $dbh = $p->{'dbh'};  | 
| 
207
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $sth; # Keep here to have avail in callbacks below  | 
| 
208
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
    if (!$dbh || $p->{'simu'}) { #   | 
| 
209
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       local $Data::Dumper::Terse = 1;  | 
| 
210
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       local $Data::Dumper::Indent = 0;  | 
| 
211
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       print(STDERR "SQL($p->{'table'}): $q\nPlaceholder Vals:".Dumper($vals)."\n");  | 
| 
212
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return(0);  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Special Return value generators  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # These should also close the statement (if that is not returned)  | 
| 
216
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
    my $rets = {  | 
| 
217
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       'count' => sub {my @a = $sth->fetchrow_array();$sth->finish();$a[0];},  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
218
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
       'sth'   => sub {return($sth);},  | 
| 
219
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
       'hash'  => sub {my $h = $sth->fetchrow_hashref();$sth->finish();$h;},  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
220
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
       'aoh'   => sub {my $arr = $sth->fetchall_arrayref({});$sth->finish();$arr;},  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
221
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    };  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # $p->{'errstr'} =return(0);  | 
| 
223
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$dbh) {die("SHP: No Connection !");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
224
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
    if ($p->{'debug'} && $vals) {print("Full Q: $q w. ".scalar(@$vals)." values\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Prepare cached ?  | 
| 
226
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $sth = $dbh->prepare_cached($q); # print("CACHED\n");  | 
| 
227
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$sth) {die("Query ($q) Not prepared (".$dbh->errstr().")\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
228
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $ok = $sth->execute(@$vals);  | 
| 
229
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$ok) {die("Failed to execute\n - Query: $q\n - Vals: ".Dumper($vals)."\n - Message:\n".$sth->errstr()."");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Special return processing.  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: Suppress Use of uninitialized value $rett in hash element  | 
| 
232
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$rett) {$rett = '';}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
233
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (my $rcb = $rets->{$rett}) {  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #print("Special return by $rett ($rcb)\n");  | 
| 
235
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return($rcb->());  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Done with statement  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    DWS:  | 
| 
239
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $sth->finish();  | 
| 
240
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return($ok);  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###################################################  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Make this the "best-possible" fallback quote when quote() method from driver (via connection)  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # is not available. The surrounding quotes are included in the quoted string.  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This aims to be SQL compliant as much as possible  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Need looks like number (will fail sometimes) ?  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub quote {  | 
| 
249
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
   my ($s) = @_;  | 
| 
250
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   $s =~ s/\'/\'\'/g;  | 
| 
251
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   $s =~ s/\n/\\n/g;  | 
| 
252
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   "'".$s."'";  | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->insert($e)  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Store entry %$e (hash) inserting it as a new entry to a database.  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns an array of ID values for the entry that got stored (array  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 of one element for numeric primary key, multiple for composite key).  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # (ref to)  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #Connection has been passed previously in construction of persister.  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #The table / schema to store to is either the one passed at  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #construction or derived from perl "blessing" of entry ($e).  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub insert {  | 
| 
268
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
6
 | 
    my ($p, $e, %c) = @_;  | 
| 
269
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    local $Data::Dumper::Terse = 1;local $Data::Dumper::Indent = 0;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # No enforced internal validation  | 
| 
271
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    eval {$p->validate();};  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
272
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    if ($@) {die("Persister validation error: $@");} # $p->{'errstr'} = $@;return(1);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
273
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (reftype($e) ne 'HASH') {die("Entry needs to be HASH");} # return(2);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Explicit attributes. Do not check for ref-valued attributes here.  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # (with an idea that caller must know what it is passing).  | 
| 
276
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (my $ats = $c{'attrs'}) {  | 
| 
277
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (ref($ats) ne 'ARRAY') {die("Passed 'attrs' must be an array");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
278
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       %$e = map({($_, $e->{$_});} @$ats); # Reconfig $e content.  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Possibly also test for references (ds branching ?) eliminating them too  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # In case some ARE found, make a copy, eliminate refs and mark copy as $e  | 
| 
282
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (grep({ref($e->{$_});} keys(%$e))) {  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Consider array serialization policy here or as a step before ?   | 
| 
284
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my %ec = map({ref($e->{$_}) ? () : ($_, $e->{$_});} keys(%$e));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
285
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $e = \%ec;  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Extract attrs and values (for non-ref attrs)  | 
| 
288
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    my @ea = sort (keys(%$e));  | 
| 
289
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    my @ev = @$e{@ea}; # map()  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # To Support sequence we MUST precalc placeholders here.  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # In case of Sequence should place sequence call ...  | 
| 
292
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @pha = map({'?';} @ea);  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
    | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Sequence - Add sequenced ID allocation ???  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # $p->{'seqname'}  | 
| 
295
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
6
 | 
    if ($p->{'autoid'} && ($p->{'autoid'} eq 'seq')) {  | 
| 
296
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my $bkt = 'Oracle';  | 
| 
297
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my @pka = pkeys($p);  | 
| 
298
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (@pka > 1) {die("Error: Multiple (composite) pkeys for sequenced ID");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Add Sequence id attibute AND sequence call (unshift to front ?)  | 
| 
300
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push(@ea, @pka); #  $p->{'pkey'}->[0]  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Lookup Sequence Syntax (as printf format)for paticular DB Backend  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Fixed INSERT Below to NOT have placeholder for sequence (placeholders calc'd above)  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # I case of sequence the counts of VALS vs @pha will be unbalanced (off by 1)  | 
| 
304
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push(@pha, sprintf("$bkmeta->{$bkt}->{'sv'}", $p->{'seqname'}) ); #   | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #DEBUG:print("FMT: $bkmeta->{$bkt}->{'sv'} / $p->{'seqname'}\n");  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
307
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if ($StoredHash::hardval) {  | 
| 
308
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #my $quote = $p->{'dbh'} ? ref($p->{'dbh'}).'::quote' : \"e;  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #DEBUG:print("QUOTE =  $quote\n\n\n\n");  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #OLD:my $quote = $p->{'dbh'}->can('quote') ? \&$p->{'dbh'}->quote : \&sqlvalesc;  | 
| 
312
 | 
0
 | 
  
  0
  
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
      my $quoter = $p->{'dbh'}->can('quote') ? sub {$p->{'dbh'}->quote($_[0]);} : \"e;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
313
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      @pha = map({  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
        #$p->{'dbh'}->quote($e->{$_});  | 
| 
315
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
        $quoter->($e->{$_}); # OLD: Embed $dbh as $_[0]  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      } @ea);  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
318
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    my $qp = "INSERT INTO $p->{'table'} (".join(',',@ea).") VALUES (".join(',', @pha).")";  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # For now $StoredHash::hardval will always return query for the very efficient $dbh->do()  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # to execute the query.  | 
| 
321
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (my $hv = $StoredHash::hardval) {  | 
| 
322
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if ($hv == 2) {return($qp);} # Return INSERT ... as-is  | 
| 
 
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       elsif ($hv == 1) {  | 
| 
324
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
          my $ok = $p->{'dbh'}->do($qp);  | 
| 
325
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
          if (!$ok) {die("Failed do on hard query: $qp");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
          # Proceed to autoid  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #DEBUG:print(Dumper($p));  | 
| 
330
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    if ($p->{'debug'}) {print(STDERR "Ins.vals: ".Dumper(\@ev)."\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
331
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (!$p->{'dbh'}) {return($qp);} # No conn. - return SQL  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
333
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $okid;  | 
| 
334
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$StoredHash::hardval) {$okid = $p->qexecute($qp, \@ev);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
335
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Auto-id - either AUTO_INC style or Sequence (works for seq. too ?)  | 
| 
337
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($p->{'autoid'}) {  | 
| 
338
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my @pka = pkeys($p);  | 
| 
339
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (@pka != 1) {die(scalar(@pka)." Keys for Autoid");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
340
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my $id = $p->fetchautoid();  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #$e->{$pka[0]} = $id;  | 
| 
342
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return(($id));  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Seq ?  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #elsif () {}  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # $p->pkeyvals($e); # wantarray ?  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {  | 
| 
348
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my @pka = pkeys($p);  | 
| 
349
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return(@$e{@pka}); # wantarray ? @$e{@pka} : [@$e{@pka}];  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->update($e, $ids, %opts);  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Update an existing entry by ID(s) ($ids) in the database with values in hash %$e.  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return  true for success, false for failure (direct $ok values from underlying  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $sth->execute() for 'autoid' => 1 ),    | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Provide protection for AUTO-ID (to not be changed) ?  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #For flexibility the $idvals may be hash or array (reference) with  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #hash containing (all) id keys and id values or alternatively array  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #containing id values IN THE SAME ORDER as keys were passed during  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #construction (with idattr/pkey parameter).  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub update {  | 
| 
368
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
688
 | 
    my ($p, $e, $idvals, %c) = @_;  | 
| 
369
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    local $Data::Dumper::Terse = 1;local $Data::Dumper::Indent = 0;  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
370
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @pka; # To be visible to closure  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Extract ID Values from hash OR array  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: Loosen requirement for hash to describe pk-attributes ?  | 
| 
373
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
    my $idvgens = {  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       'HASH'  => sub {@$idvals{@pka};},  | 
| 
375
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
 
 | 
3
 | 
       'ARRAY' => sub {return(@$idvals);},  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #'' => sub {[$idvals];}  | 
| 
377
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
    };  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # No mandatory (internal) validation ?  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #eval {$p->validate();};if ($@) {$p->{'errstr'} = $@;return(1);}  | 
| 
380
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    @pka = pkeys($p); # PKs from Persister  | 
| 
381
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    if (reftype($e) ne 'HASH') {die("Entry not passed as hash");} # {$p->{'errstr'} = "Entry needs to be hash";return(2);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Probe the type of $idvals  | 
| 
383
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    my $idrt = reftype($idvals);  | 
| 
384
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if ($p->{'debug'}) {print("Got IDs:".Dumper($idvals)." as '$idrt'\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my @idv;  | 
| 
386
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @pkv; # PK Values  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Handle kw params for bulk updates ? Example: 'w' => {...} if (!$idrt {$widstr = wherefilter();}    | 
| 
388
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (my $idg = $idvgens->{$idrt}) {@pkv = $idg->();}  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #VERYOLD:if ($idrt ne 'HASH') {$p->{'errstr'} = "ID needs to be hash";return(3);}  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {die("Need IDs as HASH or ARRAY (reference, got '$idrt')");}  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my ($cnt_a, $cnt_v) = (scalar(@pka), scalar(@pkv));  | 
| 
392
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (@pkv != @pka) {die("Number of ID keys and ID values (".scalar(@pka).'/'.scalar(@pkv).") not matching for update ($p->{'table'})");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #OLDSIMPLE: my @ea = sort(keys(%$e));  | 
| 
394
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @ea;  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Leave to caller to check: Verify that we DO NOT HAVE pkeys in set (?)  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my $drive = 0;  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Comply to explicit attributes passed as 'attrs'  | 
| 
398
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    if (ref($c{'attrs'}) eq 'ARRAY') {  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #print("DRIVE ATTRIBUTES: ");  | 
| 
400
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      @ea = @{$c{'attrs'}};  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #$drive = 1;  | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Use natural attributes from entry.  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {@ea = sort(keys(%$e));}  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #print("DRIVE ATTRIBUTES = $drive: @ea\n");exit(1);  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my @pkv = @$idh{@pka}; # $idvals, Does not work for hash  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Check for undef/empty ID comps  | 
| 
408
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    if (my @badid = $p->invalidids(@pkv)) {  | 
| 
409
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $p->{'errstr'} = "Bad ID Values found (@badid)";return(4);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
411
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    my $widstr = whereid($p, $StoredHash::hardval ? (\@pkv) : () );  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Persistent object type  | 
| 
413
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    my $pot = $p->{'table'};  | 
| 
414
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (!$pot) {die("No table for update");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #   | 
| 
416
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    my $qp = "UPDATE $pot SET ".join(',', map({" $_ = ?";} @ea)).  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       " WHERE $widstr";  | 
| 
418
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my $dbh = $p->{'dbh'};  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
420
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (my $hv = $StoredHash::hardval) {  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #my $quote = $p->{'dbh'} ? ref($p->{'dbh'}).'::quote' : \"e;  | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #TODO:  | 
| 
423
 | 
0
 | 
  
  0
  
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
      my $quoter = $p->{'dbh'}->can('quote') ? sub {$p->{'dbh'}->quote($_[0]);} : \"e;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
424
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $set = join(',', map({  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         #" $_ = ".$dbh->quote($e->{$_});  | 
| 
426
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         " $_ = ".$quoter->($e->{$_});  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      } @ea) );  | 
| 
428
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $qp = "UPDATE $pot SET $set WHERE $widstr"; # hard values embedded by whereid()  | 
| 
429
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if (!@pkv) {die("No ID:s for hardval=$StoredHash::hardval");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
430
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if ($hv == 2) {return($qp);}  | 
| 
 
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #elsif ($hv == 1) {return $dbh->do($qp);}  | 
| 
432
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      elsif ($hv == 1) {  | 
| 
433
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $ok = eval {$dbh->do($qp);};  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
434
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if (!$ok) {die("Error DO(SQL: $qp): ".$@);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
435
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         return($ok);  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      }  | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Combine Entry attr values and primary key values  | 
| 
439
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    my $allv = [@$e{@ea}, @pkv];  | 
| 
440
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if ($p->{'debug'}) {print("Update allvals: ".Dumper($allv)."\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
441
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (!$p->{'dbh'}) {return($qp);}  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
442
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $ok;  | 
| 
443
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    eval {  | 
| 
444
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $ok = $p->qexecute($qp, $allv);  | 
| 
445
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    };  | 
| 
446
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($@) {die("Error Executing: ".$p->{'dbh'}->errstr()."\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
447
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Check all natural IDs (separate case for composite ?)  | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (!$p->{'autoid'}) {  | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #     | 
| 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #}  | 
| 
451
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return($ok);  | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->delete($ids) OR $shp->delete($filter)  | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
456
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Delete an entry from database by passing one of the following:  | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
458
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
459
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * $ids - array with ID(s) for entry to be deleted (the usual use-case)  | 
| 
461
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
462
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * $filter - a hash with a where filter condition to delete by.  | 
| 
463
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
464
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Note that passing $filter haphazardly can cause massive destruction. Try to stick with passing $ids.  | 
| 
467
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
468
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
469
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
470
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # (filter) ... containing (all) primary key(s) and their values)  | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * array @$e - One or many primary key values for entry to be deleted  | 
| 
472
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #The recommended use is case "array" as it is most versatile and most  | 
| 
473
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #consistent with other API methods.  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub delete {  | 
| 
476
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
643
 | 
    my ($p, $e) = @_;  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (!ref($p->{'pkey'})) {die("PKA Not Known");}  | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #eval {$p->validate();};if ($@) {$p->{'errstr'} = $@;return(1);}  | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my @pka = @{$p->{'pkey'}};   | 
| 
480
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    my @pka = pkeys($p);  | 
| 
481
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    if (!$e) {die("Must have ID or filter for delete()\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
483
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    my $rt = reftype($e); # Allows blessed  | 
| 
484
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    my $pkc = $p->pkeycnt();  | 
| 
485
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @pkv;my $wstr;  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # $e Scalar, must have 1 pkey. Allow this forgiving behaviour for now.  | 
| 
487
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
7
 | 
    if (!$rt && ($pkc == 1)) {$e = [$e];$rt = 'ARRAY';} # OLD: {@pkv = $e;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
488
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Hash - OLD: extract primary keys @pkv = @$e{@pka};  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # NEW: treat as filter  | 
| 
490
 | 
2
 | 
  
 50
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
3
 | 
    if ($rt eq 'HASH') {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
491
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
       if (!%$e) {die("Will not delete by empty filter (HASH) !");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # TODO: Share filter-case with load(), count()  | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #my @ks = sort(keys(%$e));  | 
| 
494
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
       my @ks = grep({!ref($e->{$_})} keys(%$e));  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
495
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
       @pkv = @$e{@ks}; # In this context @vs => @pkv - Not really vals for primary keys, but filter  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #NOT:$wstr = wherefilter($e);  | 
| 
497
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       $wstr = join(' AND ', map({"$_ = ?";} @ks));  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
498
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    } #   | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Array (of pk values) - check count matches  | 
| 
500
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    elsif (($rt eq 'ARRAY') && ($pkc == scalar(@$e))) {  | 
| 
501
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       @pkv = @$e;  | 
| 
502
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $wstr = whereid($p);  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {die("No way to delete (without  ARRAY for IDs or HASH for filter)\n");}  | 
| 
505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #NOTNEEDED:#my %pkh;@pkh{@pka} = @pkv;  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my $wstr = join(' AND ', map({"$_ = ?";} @pka));  | 
| 
507
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (!$wstr) {die("Not proceding to delete with empty filter !");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
508
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    my $qp = "DELETE FROM $p->{'table'} WHERE $wstr";  | 
| 
509
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (!$p->{'dbh'}) {return($qp);}  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
510
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->qexecute($qp, \@pkv);  | 
| 
511
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
512
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #my $dbh = $p->{'dbh'};  | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #my $sth = $dbh->prepare($qp);  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #if (!$sth) {print("Not prepared\n");}  | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #$sth->execute(@pkv);  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->exists($ids)  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Test if an entry exists in the DB table with ID values passed in @$ids (array).  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns 1 (entry exists) or 0 (does not exist) under normal conditions.  | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub exists {  | 
| 
524
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
649
 | 
    my ($p, $ids) = @_;  | 
| 
525
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    my $whereid = $p->{'where'} ? $p->{'where'} : whereid($p);  | 
| 
526
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    my $qp = "SELECT COUNT(*) FROM $p->{'table'} WHERE $whereid";  | 
| 
527
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    if (!$p->{'dbh'}) {return($qp);}  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
528
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->qexecute($qp, $ids, 'count');  | 
| 
529
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
530
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
531
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->load($ids)  | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Load entry from DB table by its IDs passed in @$ids (array,   | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 single id typical sequece autoid pkey, multiple for composite primary key).  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Entry will be loaded from single table passed at construction  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 (never as result of join from multiple tables).  | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return entry as a hash (ref).  | 
| 
539
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
541
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub load {  | 
| 
542
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
685
 | 
    my ($p, $ids) = @_;  | 
| 
543
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    my $whereid = $p->{'where'} ? $p->{'where'} : whereid($p);  | 
| 
544
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Allow loading unique entry generic filter  | 
| 
545
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    if (reftype($ids) eq 'HASH') {  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #$whereid = wherefilter($ids);  | 
| 
547
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my @ks = grep({!ref($ids->{$_})} keys(%$ids));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
548
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my @vs = @$ids{@ks};  | 
| 
549
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $whereid = join(' AND ', map({"$_ = ?";} @ks));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
550
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $ids = \@vs;  | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Need hard (unique values) for Certain DBs  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # TODO: Move to neater abstraction for $StoredHash::hardval  | 
| 
553
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
      if (my $hv = $StoredHash::hardval && $p->{'dbh'}) {  | 
| 
554
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $i = -1;  | 
| 
555
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $dbh = $p->{'dbh'};  | 
| 
556
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $whereid = join(' AND ', map({$i++;"$_ = ".$dbh->quote($vs[$i]);} @ks));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
557
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $ids = undef;  | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      }  | 
| 
559
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
560
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    my $qp = "SELECT * FROM $p->{'table'} WHERE $whereid";  | 
| 
561
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (!$p->{'dbh'}) {return($qp);}  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    | 
| 
562
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->qexecute($qp, $ids, 'hash');  | 
| 
563
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (my $c = $p->{'class'}) {return(bless($h, $c));}  | 
| 
564
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
565
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
566
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->loadset($filter, $sort, %opts);  | 
| 
567
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Load a set of Entries from persistent storage.  | 
| 
569
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Optionally provide simple "where filter hash" ($filter), whose key-value criteria  | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 is ANDed together to form the filter. Allow attibutes (in $sort, arrayref) to define sorting for entry set.  | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Allow %opts to contain 'attrs' (arrayref) to explicitly to define ettributes to load for each entry.  | 
| 
572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return set / collection of entries as array of hashes (AoH).  | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub loadset {  | 
| 
576
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
    my ($p, $h, $sort, %c) = @_; # filter, sortby  | 
| 
577
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $w = '';  | 
| 
578
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $s = '';  | 
| 
579
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # if (@_ = 2 && ref($_[1]) eq 'HASH') {}  | 
| 
580
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($h) {  | 
| 
581
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $vals = []; # Parameteric values  | 
| 
582
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $wf = wherefilter($h); # 'vals' => $vals  | 
| 
583
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if (!$wf) {die("Empty Filter !");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
584
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $w = " WHERE $wf";  | 
| 
585
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
586
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: How to trigger DESC sorting (Something in %c OR first or last elem of $sort) ?  | 
| 
587
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
    if (ref($sort) && @$sort) {  | 
| 
588
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $stype = ''; # Default in SQL:   | 
| 
589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #if ($sort->[0] eq '') {}  | 
| 
590
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $s = ' ORDER BY '.join(',', @$sort);  | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
592
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($p->{'debug'}) {print("Loading set by '$w'\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
593
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $fldstr = '*';  | 
| 
594
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (ref($c{'attrs'}) eq 'ARRAY') {  | 
| 
595
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $fldstr = join(',', @{$c{'attrs'}});  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
596
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
597
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $qp = "SELECT $fldstr FROM $p->{'table'} $w $s";  | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Clean up query by (?):  | 
| 
599
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $qp =~ s/\s+$//;  | 
| 
600
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->qexecute($qp, undef, 'aoh');  | 
| 
601
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
602
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->cols(%opts)  | 
| 
603
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
604
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Sample Column names from (current) DB table.  | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return (ref to) array with field names in it.  | 
| 
606
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 %opts may contain KW parameter 'full' to get full DBI column_info() structure (See DBI for details).  | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
609
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
610
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub cols {  | 
| 
611
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($p, %c) = @_;  | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
613
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Alternative for full table schema info  | 
| 
614
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: 'fullinfo' => 1 or 'meta'  | 
| 
615
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($c{'full'}) {  | 
| 
616
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $dbh = $p->{'dbh'};  | 
| 
617
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $sth = $dbh->column_info(undef, undef, $p->{'table'}, '%');  | 
| 
618
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $fullinfo = $sth->fetchall_arrayref({});  | 
| 
619
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $sth->finish();  | 
| 
620
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      return($fullinfo);  | 
| 
621
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
622
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Likely Most portable way of quering cols  | 
| 
623
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $qp = "SELECT * FROM $p->{'table'} WHERE 1 = 0";  | 
| 
624
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $sth = $p->qexecute($qp, undef, 'sth');  | 
| 
625
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $cols = $sth->{'NAME'};  | 
| 
626
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (@_ == 1) {$sth->finish();return($cols);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #elsif (@_ == 2) {$rett = $_[1];};  | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if ($rett ne 'meta') {return(undef);}  | 
| 
629
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return(undef);  | 
| 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
631
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
632
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Load "tree" of entries rooted at an entry / entries (?)  | 
| 
633
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns a set (array) of entries or single (root entry if  | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # option $c{'fsingle'} - force single - is set.  | 
| 
635
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub loadtree {  | 
| 
636
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($p, %c) = @_;  | 
| 
637
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $chts = $c{'ctypes'};  | 
| 
638
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $w = $c{'w'};  | 
| 
639
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $fsingle = $c{'fsingle'}; # singleroot, uniroot  | 
| 
640
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $arr = loadset($p, $w);  | 
| 
641
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    for my $e (@$arr) {my $err = loadchildern($p, $e, %c);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
642
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Choose return type  | 
| 
643
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if ($fsingle) {return($arr->[0]);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
644
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return($arr);  | 
| 
645
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
646
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
647
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Load Instances of child object types for entry.  | 
| 
648
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Child types are defined in 'ctypes' array(ref) in options.  | 
| 
649
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Array 'ctypes' may be one of the following  | 
| 
650
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * Plain child type names (array of scalars), the rest is guessed  | 
| 
651
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * Array of child type definition hashes with hashes defining following:  | 
| 
652
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  =item * table   - The table / objectspace of child type  | 
| 
653
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  =item * parkey  - Parent id field in child ("foreign key" field in rel DBs)  | 
| 
654
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  =item * memname - Mamber name to place the child collection into in parent entry  | 
| 
655
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * Array of arrays with inner arrays containing 'table','parkey','memname' in  | 
| 
656
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   that order(!), (see above for meanings)  | 
| 
657
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Return 0 for no errors  | 
| 
658
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
659
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Maintain persister cache with rudimentary relational info.  | 
| 
660
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub loadchildren {  | 
| 
661
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
   my ($p, $e, %c) = @_;  | 
| 
662
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my $chts = $c{'ctypes'};  | 
| 
663
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   if (!$chts) {die("No Child types indicated");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
664
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   if (ref($chts) ne 'ARRAY') {die("Child types not ARRAY");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
665
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my @ids = pkeyvals($p, $e);  | 
| 
666
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   if (@ids > 1) {die("Loading not supported for composite keys");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
667
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my $dbh = $p->{'dbh'};  | 
| 
668
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   my $debug = $p->{'debug'};  | 
| 
669
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   for (@$chts) {  | 
| 
670
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #my $ct = $_;  | 
| 
671
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $cfilter;  | 
| 
672
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Use or create a complete hash ?  | 
| 
673
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $cinfo = makecinfo($p, $_);  | 
| 
674
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if ($debug) {print(Dumper($cinfo));}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
675
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Load type by created filter  | 
| 
676
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my ($ct, $park, $memn) = @$cinfo{'table','parkey','memname',};  | 
| 
677
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if (!$park) {}  | 
| 
678
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Create where by parkey info  | 
| 
679
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #$cfilter = {$park => $ids[0]}; # What is par key - assume same as parent  | 
| 
680
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if (@$park != @ids) {die("Par and child key counts mismatch");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
681
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      @$cfilter{@$park} = @ids;  | 
| 
682
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #my $cfilter =   | 
| 
683
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Take a shortcut by not providing pkey  | 
| 
684
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $shc = StoredHash->new('table' => $ct, 'pkey' => [],  | 
| 
685
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         'dbh' => $dbh, 'loose' => 1, 'debug' => $debug);  | 
| 
686
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $carr = $shc->loadset($cfilter);  | 
| 
687
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
      if (!$carr || !@$carr) {next;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
688
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #if ($debug) {print("Got Children".Dumper($arr));}  | 
| 
689
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      $e->{$memn} = $carr;  | 
| 
690
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Blessing  | 
| 
691
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      if (my $bto = $cinfo->{'blessto'}) {map({bless($_, $bto);} @$carr);}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
692
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Circular Ref from child to parent ?  | 
| 
693
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #if (my $pla = $cinfo->{'parlinkattr'}) {map({$_->{$pla} = $e;} @$carr);}  | 
| 
694
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
695
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # Autobless Children ?  | 
| 
696
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   return(0);  | 
| 
697
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
698
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
699
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
700
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal method for using or making up Child relationship information  | 
| 
701
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # for loading related entities.  | 
| 
702
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub makecinfo {  | 
| 
703
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($p, $cv) = @_;  | 
| 
704
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Support array with: 'table','parkey','memname'  | 
| 
705
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (ref($cv) eq 'ARRAY') {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
706
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my $cinfo;  | 
| 
707
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (@$cv != 3) {die("Need table, parkey, memname in array");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
708
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       @$cinfo{'table','parkey','memname'} = @$cv;  | 
| 
709
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return($cinfo);  | 
| 
710
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
711
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Assume all is there (could validate and provide missing)  | 
| 
712
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    elsif (ref($cv) eq 'HASH') {  | 
| 
713
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my @a = ('table','parkey','memname');  | 
| 
714
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Try guess parkey ?  | 
| 
715
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (!$cv->{'parkey'}) {$cv->{'parkey'} = [pkeys($p)];}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
716
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       for (@a) {if (!$cv->{$_}) {die("Missing '$_' in cinfo");}}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
717
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       return($cv);  | 
| 
718
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
719
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    elsif (ref($cv) ne '') {die("child type Not scalar (or hash)");}  | 
| 
720
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    ################## Make up  | 
| 
721
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $ctab = $cv;  | 
| 
722
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $memname = $ctab; # Default memname to child type name (Plus 's') ?  | 
| 
723
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Guess by parent  | 
| 
724
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $parkey = [pkeys($p)];  | 
| 
725
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $cinfo = {'table' => $ctab, 'parkey' => $parkey, 'memname' => $ctab,};  | 
| 
726
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return($cinfo);  | 
| 
727
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
728
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
729
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ###################################################################  | 
| 
730
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
731
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal Persister validator for the absolutely mandatory properties of  | 
| 
732
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # persister object itself.  | 
| 
733
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Doesn't not validate entry  | 
| 
734
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub validate {  | 
| 
735
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
2
 | 
    my ($p) = @_;  | 
| 
736
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    if (ref($p->{'pkey'}) ne 'ARRAY') {die("PK Attributes Not Known\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
737
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Allow table to come from blessing (so NOT required)  | 
| 
738
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (!$p->{'table'}) {die("No Table\n");}  | 
| 
739
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    if ($p->{'simu'}) {return;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
740
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Do NOT Require conenction  | 
| 
741
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (!ref($p->{'dbh'})) {die("NO dbh to act on\n");} # ne 'DBI'  | 
| 
742
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
743
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
744
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
745
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=head2 @pka = $shp->pkeys()  | 
| 
746
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
747
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal method for returning  array of id keys (Real array, not ref).  | 
| 
748
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
749
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=cut  | 
| 
750
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub pkeys {  | 
| 
751
 | 
11
 | 
 
 | 
 
 | 
  
11
  
 | 
  
0
  
 | 
12
 | 
    my ($p) = @_;  | 
| 
752
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
    my $prt = reftype($p);  | 
| 
753
 | 
11
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    if ($prt ne 'HASH') {  | 
| 
754
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $|=1;  | 
| 
755
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       print(STDERR Dumper([caller(1)]));  | 
| 
756
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       die("StoredHash Not a HASH (is '$p'/'$prt')");  | 
| 
757
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
758
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Excessive validation ?  | 
| 
759
 | 
11
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
23
 | 
    if (ref($p->{'pkey'}) ne 'ARRAY') {die("Primary keys not in an array");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
760
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #return($p->{'pkey'});  | 
| 
761
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    return(@{$p->{'pkey'}});  | 
| 
 
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
    | 
| 
762
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
763
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
764
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->count($filter)  | 
| 
765
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
766
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Get Count of all or a filtered set of entries (by optional $filter) in table.  | 
| 
767
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return (scalar) count of entries.  | 
| 
768
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
769
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
770
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub count {  | 
| 
771
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
1
  
 | 
1985
 | 
    my ($p, $fh) = @_; # $fh - Filter Hash  | 
| 
772
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    my $qc = "SELECT COUNT(*) FROM $p->{'table'}";  | 
| 
773
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: See filter case for load(), delete()  | 
| 
774
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Use it and replace 2nd param of qexecute w. params  | 
| 
775
 | 
4
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
19
 | 
    if (ref($fh) eq 'HASH' && keys(%$fh)) {  | 
| 
776
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
       my $w = wherefilter($fh); # my ($w, $vals) = wherefilter_para($fh);  | 
| 
777
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
       $qc .= " WHERE $w";  | 
| 
778
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
779
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    if ($p->{'debug'}) {print("Count Query:$qc\n");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
780
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    if (!$p->{'dbh'}) {return($qc);}  | 
| 
 
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
781
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $p->qexecute($qc, undef, 'count'); # $vals  | 
| 
782
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
783
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
784
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 INTERNAL METHODS  | 
| 
785
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
786
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 These methods you should not need working on the high level. However for the curious they are outlined here.  | 
| 
787
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
788
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 @pkv = $shp->pkeyvals($e)  | 
| 
789
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
790
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return Primary key values (as "real" array, not ref to one) from hash %$e.  | 
| 
791
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 undef values are produced for non-existing keys.  | 
| 
792
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Mostly used for internal operations (and possibly debugging).  | 
| 
793
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
794
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
795
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub pkeyvals {  | 
| 
796
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
    my ($p, $e) = @_;  | 
| 
797
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my @pkeys = pkeys($p);  | 
| 
798
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    @$e{@pkeys};  | 
| 
799
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
800
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
801
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Implement pulling last id from sequence  | 
| 
802
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub fetchautoid {  | 
| 
803
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($p) = @_;  | 
| 
804
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $dbh;  | 
| 
805
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #$dbh->{'Driver'}; # Need to test ?  | 
| 
806
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #DEV:print("AUTOID FETCH TO BE IMPLEMENTED\n");return(69);  | 
| 
807
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $pot = $p->{'table'};  | 
| 
808
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!$pot) {die("No table for fetching auto-ID");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
809
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (!($dbh = $p->{'dbh'})) {die("No Connection for fetching ID");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
810
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    $dbh->last_insert_id(undef, undef, $pot, undef);  | 
| 
811
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
812
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
813
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub pkeycnt {  | 
| 
814
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
2
 | 
    my ($p) = @_;  | 
| 
815
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (ref($p->{'pkey'}) ne 'ARRAY') {die("Primary keys not in an array");}  | 
| 
816
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #scalar(@{$p->{'pkey'}});  | 
| 
817
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    my @pkeys = pkeys($p);  | 
| 
818
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    scalar(@pkeys);  | 
| 
819
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
820
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
821
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal method for checking for empty or undefined ID values.  | 
| 
822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # In all reasonable databases and apps these are not valid values.  | 
| 
823
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub invalidids {  | 
| 
824
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
3
 | 
    my ($p, @idv) = @_;  | 
| 
825
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @badid = grep({!defined($_) || $_ eq '';} @idv);  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
826
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    return(@badid);  | 
| 
827
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
828
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
829
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $shp->whereid($pkvals);  | 
| 
830
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
831
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Generate SQL WHERE Clause for update() (or delete() or load() or exists()) based on primary keys of current (table) type.  | 
| 
832
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Return WHERE clause with id-attribute(s) and placeholder(s) (idkey = ?, ...), without the WHERE keyword.  | 
| 
833
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Mostly called for internal operations. You should not need this.  | 
| 
834
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
835
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
836
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub whereid {  | 
| 
837
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
1
  
 | 
6
 | 
    my ($p, $pkval) = @_;  | 
| 
838
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # # Allow IDs to be hash OR array ?? Not because hash would req. to store order  | 
| 
839
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    my @pka = pkeys($p);  | 
| 
840
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
426
 | 
    if (@pka < 1) {die("No Pkeys to create where ID clause");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
841
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # my $wstr =   | 
| 
842
 | 
5
 | 
  
  0
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
10
 | 
    if ($pkval && (ref($pkval) eq 'ARRAY') && (@$pkval == @pka) ) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
843
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # TODO: Mock DBI  | 
| 
844
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $dbh = $p->{'dbh'};  | 
| 
845
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      # Can't use string ("DBI::db::quote") as a subroutine ref while "strict refs" in use  | 
| 
846
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
      my $quote = $p->{'dbh'} ?  | 
| 
847
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         #ref($p->{'dbh'}).'::quote'  | 
| 
848
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         sub {$p->{'dbh'}->quote($_[0]);}  | 
| 
849
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         : \"e;  | 
| 
850
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $i = -1;  | 
| 
851
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      #no strict 'refs';  | 
| 
852
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my $wif = join(' AND ', map({  | 
| 
853
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $i++;"$_ = ".$quote->($pkval->[$i]);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
854
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      } @pka));  | 
| 
855
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      return $wif; # OLD (nostrict) $wid  | 
| 
856
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
857
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
    return join(' AND ', map({"$_ = ?";} @pka));  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
    | 
| 
858
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
859
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
860
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal fallback method to escape (string) value. Prefer using $dbh->quote() if $dbh is  | 
| 
861
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # handle available and the associated DBD Driver supports it.  | 
| 
862
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # The first parameter to this is an unused dummy parameter to match call of $dbh->quote($str).  | 
| 
863
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # This does not place surrounding quotes on the value returned.  | 
| 
864
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Return value properly escaped.  | 
| 
865
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Cover all scenarios  | 
| 
866
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub sqlvalesc {  | 
| 
867
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
2
 | 
    my ($foo, $v) = @_;  | 
| 
868
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #$v =~ s/'/\\'/g; # $str =~ s/'/''/g;  | 
| 
869
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    $v =~ s/\'/\'\'/g;  | 
| 
870
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    $v =~ s/\n/\\n/g;  | 
| 
871
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    $v;  | 
| 
872
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
873
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
874
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Create list for WHERE IN Clause based on some assumptions  | 
| 
875
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub invalues {  | 
| 
876
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($vals) = @_;  | 
| 
877
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Assume array ref validated outside  | 
| 
878
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (ref($vals) ne 'ARRAY') {die("Not an array for invals");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
879
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Escape within Quotes ?  | 
| 
880
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    join(',', map({  | 
| 
881
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if (/^\d+$/) {$_;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
882
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       else {  | 
| 
883
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         my $v = sqlvalesc(undef, $_);  | 
| 
884
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         "'$v'";  | 
| 
885
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
886
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    } @$vals));  | 
| 
887
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
888
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
889
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub rangefilter {  | 
| 
890
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($attr, $v) = @_;  | 
| 
891
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (ref($v) ne 'ARRAY') {die("Need value range as ARRAY of 2 elems");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
892
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Or just even and sort, grab 2 at the time ?  | 
| 
893
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (@$v != 2) {die("Range cannot be formed - need exactly 2 elements");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
894
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
    if (!defined($v->[0]) || !defined($v->[1]) ) {die("Missing either of the values ($v->[0], $v->[0])");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
895
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Auto-arrange ??? Test for both being numbers  | 
| 
896
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my @nums = map({Scalar::Util::looks_like_number($_) ? (1) : ();} @$v);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
897
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    if (@nums == 2) {  | 
| 
898
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       if ($v->[1] < $v->[0]) {$v = [$v->[1],$v->[0]];}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
899
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
900
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Detect need to escape (time vs. number)  | 
| 
901
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return "($attr >= $v->[0]) AND ($attr <= $v->[1])";  | 
| 
902
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #return " $attr BETWEEN $v->[0] AND $v->[1]";  | 
| 
903
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
904
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
905
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=head2  StoredHash::wherefilter($e,%c);  | 
| 
906
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
907
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Generate simple WHERE filter by hash %$e. The keys are assumed to be attributes  | 
| 
908
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # of DB and values are embedded as values into SQL (as opposed to using placeholers).  | 
| 
909
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # To be perfect in escaping per attribute type info would be needed.  | 
| 
910
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # For now we do best effort heuristics (attr val \d+ is assumed  | 
| 
911
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # to be a numeric field in SQL, however 000002345 could actually   | 
| 
912
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # be content of a char/text/varchar field).  | 
| 
913
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Return WHERE filter clause without WHERE keyword.  | 
| 
914
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub wherefilter {  | 
| 
915
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
3
 | 
    my ($e, %c) = @_;  | 
| 
916
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    my $w = '';  | 
| 
917
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my $fop = ' AND ';  | 
| 
918
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my $rnga = $c{'rnga'}; # Range attributes  | 
| 
919
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
    if (ref($e) ne 'HASH') {die("No hash for filter generation");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
920
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Ensure deterministic order  | 
| 
921
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    my @keys = sort keys(%$e);  | 
| 
922
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    my @qc; # Query Components  | 
| 
923
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Assume hard values, treat everything as string (?)  | 
| 
924
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # TODO: forcestr ?  | 
| 
925
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
    @qc = map({  | 
| 
926
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       my $v = $e->{$_};  | 
| 
927
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #my $rv = ref($v);  | 
| 
928
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       #if ($rnga->{$_} && ($rv eq 'ARRAY') && (@$v == 2)) {rangefilter($_, $v);}  | 
| 
929
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # For now, assume IN - clause  | 
| 
930
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
       if (ref($v) eq 'ARRAY') {" $_ IN (".invalues($v).") ";}  | 
| 
 
 | 
0
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
931
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # SQL Wildcard  | 
| 
932
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       elsif ($v =~ /%/) {"$_ LIKE '$v'";}  | 
| 
933
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Detect numeric (likely numeric, not perfect)  | 
| 
934
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # TODO: Explicit param to   | 
| 
935
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
       elsif ($v =~ /^\d+$/) {"$_ = $v";}  | 
| 
936
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Assume string  | 
| 
937
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       else {"$_ = '".sqlvalesc(undef, $v)."'";}  | 
| 
938
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         | 
| 
939
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    } @keys);  | 
| 
940
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Create PARAMETRIC query  | 
| 
941
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    if (ref $c{'vals'} eq 'ARRAY') {  | 
| 
942
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      my @vals = ();  | 
| 
943
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      map({  | 
| 
944
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
        my $v = $e->{$_};  | 
| 
945
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
        if (ref($v) eq 'ARRAY') {push(@vals, @$v);" $_ IN (".join(',', map({"?";} @$v)).") ";}  | 
| 
 
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
946
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
        elsif ($v =~ /%/) {push(@vals, $v);"$_ LIKE ?";}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
947
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
        else {push(@vals, $v);"$_ = ?";}  | 
| 
948
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      } @keys);  | 
| 
949
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      push(@{$c{'vals'}}, @vals);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
950
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
951
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    return(join($fop, @qc)); # join by AND  | 
| 
952
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
953
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
954
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=head2 my ($where, $para) = wherefilter_para($e);  | 
| 
955
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Where filter for parametric query (for load(), delete() count())  | 
| 
956
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Return WHERE clause (without 'WHERE') and parametric values.  | 
| 
957
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Throw exception on empty %$e or ... (empty filter)  | 
| 
958
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Caller should not simply check the count of keys in hash as ref  | 
| 
959
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # valued key-pairs are skipped here.  | 
| 
960
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub wherefilter_para {  | 
| 
961
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
    my ($e) = @_;  | 
| 
962
 | 
0
 | 
  
  0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
    if (!$e || !%$e) {die("Will not generate filter by no HASH / empty HASH !");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
963
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my @ks = grep({!ref($e->{$_})} keys(%$e));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
964
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my @vs = @$e{@ks}; # In this context @vs => @pkv - Not really vals for primary keys, but filter  | 
| 
965
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    my $wstr = join(' AND ', map({"$_ = ?";} @ks));  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
966
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if (!$wstr || $wstr =~ /^\s*$/) {die("Will not generate empty filter clause");}  | 
| 
967
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    return($wstr, \@vs);  | 
| 
968
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
969
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
970
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Internal: Serialize all values (singles,multi) from a hash to an array  | 
| 
971
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # based on sorted key order. Multi-valued keys (with value being array reference)  | 
| 
972
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # add multiple items.   | 
| 
973
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub allentvals {  | 
| 
974
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
11
 | 
    my ($h) = @_;  | 
| 
975
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    map({  | 
| 
976
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
      if (ref($h->{$_}) eq 'HASH') {();}  | 
| 
 
 | 
6
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
977
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
      elsif (ref($h->{$_}) eq 'ARRAY') {@{$h->{$_}};}  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
978
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      else {($h->{$_});}  | 
| 
979
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    } sort(keys(%$h)));  | 
| 
980
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
981
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
982
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
983
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: Move to util ?  | 
| 
984
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=head2 $p->dbtabinfo(%opts) OR StoredHash::dbtabinfo($dbh, %opts);  | 
| 
985
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Covenience method for $dbh->table_info()  | 
| 
986
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Options:  | 
| 
987
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #=item * tabonly - Filter out all DB Objects where TABLE_TYPE is not 'TABLE'  | 
| 
988
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
989
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Return AoH where each of inner hashes are info for single table. Property names are in  | 
| 
990
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # standard DBI table_info() format (see perldoc DBI).  | 
| 
991
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dbtabinfo {  | 
| 
992
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
    my (%c) = @_;  | 
| 
993
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my ($p, $pdbh);  | 
| 
994
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $rt = reftype($_[0]);  | 
| 
995
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if ($rt eq 'StoredHash') {$p = shift();%c = @_;}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
996
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # elsif ($rt eq '')  | 
| 
997
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    else {$pdbh = shift();%c = @_;}  | 
| 
998
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
    my $dbh = $pdbh || $p->{'dbh'} || $c{'dbh'};  | 
| 
999
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if (!$dbh) {die("No Connection for table info");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1000
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $sth = $dbh->table_info();  | 
| 
1001
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $tabinfo = $sth->fetchall_arrayref({}); # AoH  | 
| 
1002
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $sth->finish();  | 
| 
1003
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    # Replace with $c{'all'} - Get all database objects (like views, indices ...)  | 
| 
1004
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if ($c{'tabonly'}) {  | 
| 
1005
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       @$tabinfo = grep({$_->{'TABLE_TYPE'} eq 'TABLE';} @$tabinfo);  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1006
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    }  | 
| 
1007
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      | 
| 
1008
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    return($tabinfo);  | 
| 
1009
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1010
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Experimental wrapper to query attributes  | 
| 
1011
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO: dbattrinfo($dbh, $tn);  | 
| 
1012
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dbattrinfo {  | 
| 
1013
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
    my ($dbh, $tn) = @_;  | 
| 
1014
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my ($p, $pdbh);  | 
| 
1015
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my $rt = reftype($_[0]);  | 
| 
1016
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #if ($rt eq 'StoredHash') {$p = shift();%c = @_;}  | 
| 
1017
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #elsif ($rt eq '') {$pdbh = shift();%c = @_;}  | 
| 
1018
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    #my $dbh = $pdbh || $p->{'dbh'} || $c{'dbh'};  | 
| 
1019
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if (!$dbh) {die("No Connection for attribute info");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1020
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    if (!$tn) {die("No table name for attribute info");}  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
1021
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $sth = $dbh->column_info(undef, undef, $tn, '%');  | 
| 
1022
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    my $arr = $sth->fetchall_arrayref({}); # AoH  | 
| 
1023
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    $sth->finish();  | 
| 
1024
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    return($arr);  | 
| 
1025
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
1026
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  |