/[gedafe]/trunk/lib/perl/Gedafe/DB.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/lib/perl/Gedafe/DB.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Mon Feb 14 18:52:26 2005 UTC (19 years, 2 months ago) by dpavlin
File size: 32163 byte(s)
import of Gedafe 1.2.2

1 dpavlin 1 # Gedafe, the Generic Database Frontend
2     # copyright (c) 2000-2003 ETH Zurich
3     # see http://isg.ee.ethz.ch/tools/gedafe/
4    
5     # released under the GNU General Public License
6    
7     package Gedafe::DB;
8     use strict;
9    
10     use Gedafe::Global qw(%g);
11    
12     use DBI;
13     use DBD::Pg 1.20; # 1.20 has constants for data types
14    
15     use vars qw(@ISA @EXPORT_OK);
16     require Exporter;
17     @ISA = qw(Exporter);
18     @EXPORT_OK = qw(
19     DB_Connect
20     DB_GetNumRecords
21     DB_FetchList
22     DB_GetRecord
23     DB_AddRecord
24     DB_UpdateRecord
25     DB_GetCombo
26     DB_DeleteRecord
27     DB_GetDefault
28     DB_ParseWidget
29     DB_ID2HID
30     DB_HID2ID
31     DB_GetBlobType
32     DB_GetBlobName
33     DB_DumpBlob
34     DB_RawField
35     DB_DumpTable
36     );
37    
38     sub DB_AddRecord($$$);
39     sub DB_Connect($$);
40     sub DB_DB2HTML($$);
41     sub DB_DeleteRecord($$$);
42     sub DB_DumpBlob($$$$);
43     sub DB_DumpTable($$$);
44     sub DB_ExecQuery($$$$$);
45     sub DB_FetchList($$);
46     sub DB_FetchListSelect($$);
47     sub DB_GetBlobName($$$$);
48     sub DB_GetBlobType($$$$);
49     sub DB_GetCombo($$$);
50     sub DB_GetDefault($$$);
51     sub DB_GetNumRecords($$);
52     sub DB_GetRecord($$$$);
53     sub DB_HID2ID($$$);
54     sub DB_ID2HID($$$);
55     sub DB_Init($$);
56     sub DB_MergeAcls($$);
57     sub DB_ParseWidget($);
58     sub DB_PrepareData($$);
59     sub DB_RawField($$$$);
60     sub DB_ReadDatabase($);
61     sub DB_ReadFields($$$);
62     sub DB_ReadTableAcls($$);
63     sub DB_ReadTables($$);
64     sub DB_Record2DB($$$$);
65     sub DB_UpdateRecord($$$);
66     sub DB_Widget($$);
67    
68     my %type_widget_map = (
69     'date' => 'text(size=12)',
70     'time' => 'text(size=12)',
71     'timestamp' => 'text(size=22)',
72     'timestamptz' => 'text(size=28)',
73     'int2' => 'text(size=6)',
74     'int4' => 'text(size=12)',
75     'int8' => 'text(size=12)',
76     'numeric' => 'text(size=12)',
77     'float4' => 'text(size=12)',
78     'float8' => 'text(size=12)',
79     'bpchar' => 'text(size=40)',
80     'text' => 'text',
81     'name' => 'text(size=20)',
82     'bool' => 'checkbox',
83     'bytea' => 'file',
84     );
85    
86     sub DB_Init($$)
87     {
88     my ($user, $pass) = @_;
89     my $dbh = DBI->connect_cached("$g{conf}{db_datasource}", $user, $pass) or
90     return undef;
91    
92     # read database
93     $g{db_database} = DB_ReadDatabase($dbh);
94    
95     # read tables
96     $g{db_tables} = DB_ReadTables($dbh, $g{db_database});
97     defined $g{db_tables} or return undef;
98    
99     # order tables
100     $g{db_tables_list} = [ sort { $g{db_tables}{$a}{desc} cmp
101     $g{db_tables}{$b}{desc} } keys %{$g{db_tables}} ];
102    
103     # read table acls
104     DB_ReadTableAcls($dbh, $g{db_tables}) or return undef;
105    
106     # read fields
107     $g{db_fields} = DB_ReadFields($dbh, $g{db_database}, $g{db_tables});
108     defined $g{db_fields} or return undef;
109    
110     # order fields
111     for my $table (@{$g{db_tables_list}}) {
112     $g{db_fields_list}{$table} =
113     [ sort { $g{db_fields}{$table}{$a}{order} <=>
114     $g{db_fields}{$table}{$b}{order} }
115     keys %{$g{db_fields}{$table}}
116     ];
117     }
118    
119     return 1;
120     }
121    
122     sub DB_ReadDatabase($)
123     {
124     my $dbh = shift;
125     my ($sth, $query, $data);
126     my %database = ();
127    
128     # PostgreSQL version
129     $query = "SELECT VERSION()";
130     $sth = $dbh->prepare($query);
131     $sth->execute() or return undef;
132     $data = $sth->fetchrow_arrayref();
133     $sth->finish;
134     if($data->[0] =~ /^PostgreSQL (\d+\.\d+)/) {
135     $database{version} = $1;
136     }
137     else {
138     # we don't support versions older than 7.0
139     # if VERSION() doesn't exist, assume 7.0
140     $database{version} = '7.0';
141     }
142    
143     # database oid
144     my $oid;
145     $query = "SELECT oid FROM pg_database WHERE datname = '$dbh->{Name}'";
146     $sth = $dbh->prepare($query);
147     $sth->execute() or die $sth->errstr;
148     $data = $sth->fetchrow_arrayref() or die $sth->errstr;
149     $oid = $data->[0];
150     $sth->finish;
151    
152     # read database name from database comment
153     $query = "SELECT description FROM pg_description WHERE objoid = $oid";
154     $sth = $dbh->prepare($query);
155     $sth->execute() or die $sth->errstr;
156     $data = $sth->fetchrow_arrayref();
157     $database{desc} = $data ? $data->[0] : $dbh->{Name};
158     $sth->finish;
159    
160     return \%database;
161     }
162    
163     sub DB_ReadTables($$)
164     {
165     my ($dbh, $database) = @_;
166     my %tables = ();
167     my ($query, $sth, $data);
168    
169     # combo
170     # 7.0: views have relkind 'r'
171     # 7.1: views have relkind 'v'
172    
173     # tables
174     if($database->{version} >= 7.3) {
175     $query = <<END;
176     SELECT c.relname
177     FROM pg_class c, pg_namespace n
178     WHERE (c.relkind = 'r' OR c.relkind = 'v')
179     AND (c.relname !~ '^pg_')
180     AND (c.relnamespace = n.oid)
181     AND (n.nspname != 'information_schema')
182     END
183     } else {
184     $query = <<END;
185     SELECT c.relname
186     FROM pg_class c
187     WHERE (c.relkind = 'r' OR c.relkind = 'v')
188     AND c.relname !~ '^pg_'
189     END
190     }
191     $sth = $dbh->prepare($query) or return undef;
192     $sth->execute() or return undef;
193     while ($data = $sth->fetchrow_arrayref()) {
194     $tables{$data->[0]} = { };
195     if($data->[0] =~ /^meta|(_list|_combo)$/) {
196     $tables{$data->[0]}{hide} = 1;
197     }
198     if($data->[0] =~ /_rep$/) {
199     $tables{$data->[0]}{report} = 1;
200     }
201     }
202     $sth->finish;
203    
204     # read table comments as descriptions
205     if($database->{version} >= 7.2) {
206     $query = <<'END';
207     SELECT c.relname, obj_description(c.oid, 'pg_class')
208     FROM pg_class c
209     WHERE (c.relkind = 'r' OR c.relkind = 'v')
210     AND c.relname !~ '^pg_'
211     END
212     }
213     else {
214     $query = <<'END';
215     SELECT c.relname, d.description
216     FROM pg_class c, pg_description d
217     WHERE (c.relkind = 'r' OR c.relkind = 'v')
218     AND c.relname !~ '^pg_'
219     AND c.oid = d.objoid
220     END
221     }
222     $sth = $dbh->prepare($query) or return undef;
223     $sth->execute() or return undef;
224     while ($data = $sth->fetchrow_arrayref()) {
225     next unless defined $tables{$data->[0]};
226     $tables{$data->[0]}{desc} = $data->[1];
227     }
228     $sth->finish;
229    
230     # set not-defined table descriptions
231     for my $table (keys %tables) {
232     next if defined $tables{$table}{desc};
233     if(exists $tables{"${table}_list"} and defined
234     $tables{"${table}_list"}{desc})
235     {
236     $tables{$table}{desc} = $tables{"${table}_list"}{desc};
237     }
238     else {
239     $tables{$table}{desc} = $table;
240     }
241     }
242    
243     # meta_tables
244     $query = 'SELECT meta_tables_table, meta_tables_attribute, meta_tables_value FROM meta_tables';
245     $sth = $dbh->prepare($query) or die $dbh->errstr;
246     $sth->execute() or die $sth->errstr;
247     while ($data = $sth->fetchrow_arrayref()) {
248     next unless defined $tables{$data->[0]};
249     my $attr = lc($data->[1]);
250     $tables{$data->[0]}{meta}{$attr}=$data->[2];
251     if($attr eq 'hide' and $data->[2]) {
252     $tables{$data->[0]}{hide}=1;
253     }
254     }
255     $sth->finish;
256    
257     return \%tables;
258     }
259    
260     sub DB_MergeAcls($$)
261     {
262     my ($a, $b) = @_;
263    
264     $a = '' unless defined $a;
265     $b = '' unless defined $a;
266     my %acls = ();
267     for(split('',$a)) {
268     $acls{$_}=1;
269     }
270     for(split('',$b)) {
271     $acls{$_}=1;
272     }
273     return join('',keys %acls);
274     }
275    
276     sub DB_ReadTableAcls($$)
277     {
278     my ($dbh, $tables) = @_;
279    
280     my ($query, $sth, $data);
281    
282     # users
283     my %db_users;
284     $query = 'SELECT usename, usesysid FROM pg_user';
285     $sth = $dbh->prepare($query) or die $dbh->errstr;
286     $sth->execute() or die $sth->errstr;
287     while ($data = $sth->fetchrow_arrayref()) {
288     $db_users{$data->[1]} = $data->[0];
289     }
290     $sth->finish;
291    
292     # groups
293     my %db_groups;
294     $query = 'SELECT groname, grolist FROM pg_group';
295     $sth = $dbh->prepare($query) or die $dbh->errstr;
296     $sth->execute() or die $sth->errstr;
297     while ($data = $sth->fetchrow_arrayref()) {
298     my $group = $data->[1];
299     if(defined $group) {
300     $group =~ s/^{(.*)}$/$1/;
301     my @g = split /,/, $group;
302     $db_groups{$data->[0]} = [@db_users{@g}];
303     }
304     else {
305     $db_groups{$data->[0]} = [];
306     }
307     }
308     $sth->finish;
309    
310     # acls
311     $query = "SELECT c.relname, c.relacl FROM pg_class c WHERE (c.relkind = 'r' OR c.relkind='v') AND relname !~ '^pg_'";
312     $sth = $dbh->prepare($query) or die $dbh->errstr;
313     $sth->execute() or die $sth->errstr;
314     while ($data = $sth->fetchrow_arrayref()) {
315     next unless defined $data->[0];
316     next unless defined $data->[1];
317     next unless defined $tables->{$data->[0]};
318     my $acldef = $data->[1];
319     $acldef =~ s/^{(.*)}$/$1/;
320     my @acldef = split(',', $acldef);
321     map { s/^"(.*)"$/$1/ } @acldef;
322     acl: for(@acldef) {
323     /(.*)=(.*)/;
324     my $who = $1; my $what = $2;
325     if($who eq '') {
326     # PUBLIC: assign permissions to all db users
327     for(values %db_users) {
328     $tables->{$data->[0]}{acls}{$_} =
329     DB_MergeAcls($tables->{$data->[0]}{acls}{$_}, $what);
330     }
331     }
332     elsif($who =~ /^group (.*)$/) {
333     # group permissions: assign to all db groups
334     for(@{$db_groups{$1}}) {
335     $tables->{$data->[0]}{acls}{$_} =
336     DB_MergeAcls($tables->{$data->[0]}{acls}{$_}, $what);
337     }
338     }
339     else {
340     # individual user: assign just to this db user
341     $tables->{$data->[0]}{acls}{$who} =
342     DB_MergeAcls($tables->{$data->[0]}{acls}{$who}, $what);
343     }
344     }
345     }
346    
347     $sth->finish;
348    
349     return 1;
350     }
351    
352     # DB_Widget: determine widget from type if not explicitely defined
353     sub DB_Widget($$)
354     {
355     my ($fields, $f) = @_;
356    
357     if(defined $f->{widget} and $f->{widget} eq 'isearch'){
358     my $r = $f->{reference};
359     my $rt = $g{db_tables}{$r};
360     defined $rt or die "table $f->{reference}, referenced from $f->{table}:$f->{field}, not found.\n";
361     if(defined $fields->{$r}{"${r}_hid"}) {
362     # Combo with HID
363     return "hidisearch(ref=$r)";
364     }
365     return "isearch(ref=$r)";
366     }
367    
368    
369     return $f->{widget} if defined $f->{widget};
370    
371     # HID and combo-boxes
372     if($f->{type} eq 'int4' or $f->{type} eq 'int8') {
373     if(defined $f->{reference}) {
374     my $r = $f->{reference};
375     my $rt = $g{db_tables}{$r};
376     defined $rt or die "table $f->{reference}, referenced from $f->{table}:$f->{field}, not found.\n";
377     my $combo = "${r}_combo";
378     if(defined $g{db_tables}{$combo}) {
379     if(defined $fields->{$r}{"${r}_hid"}) {
380     # Combo with HID
381     return "hidcombo(combo=$combo,ref=$r)";
382     }
383     return "idcombo(combo=$combo)";
384     }
385     if(defined $fields->{$r}{"${r}_hid"}) {
386     # Plain with HID
387     return "hid(ref=$r)";
388     }
389     return "text";
390     }
391     return $type_widget_map{$f->{type}};
392     }
393     elsif($f->{type} eq 'varchar') {
394     my $len = $f->{atttypmod}-4;
395     if($len <= 0) {
396     return 'text';
397     }
398     else {
399     return "text(size=$len,maxlength=$len)";
400     }
401     }
402     else {
403     my $w = $type_widget_map{$f->{type}};
404     defined $w or die "unknown widget for type $f->{type} ($f->{table}:$f->{field}).\n";
405     return $w;
406     }
407     }
408    
409     # Parse widget specification, split args, verify if it is a valid widget
410     sub DB_ParseWidget($)
411     {
412     my ($widget) = @_;
413     $widget =~ /^(\w+)(\((.*)\))?$/ or die "syntax error for widget: $widget";
414     my ($type, $args_str) = ($1, $3);
415     my %args=();
416     if(defined $args_str) {
417     for my $w (split('\s*,\s*',$args_str)) {
418     $w =~ s/^\s+//;
419     $w =~ s/\s+$//;
420     $w =~ /^(\w+)\s*=\s*(.*)$/ or die "syntax error in $type-widget argument: $w";
421     $args{$1}=$2;
422     }
423     }
424    
425     # verify
426     if($type eq 'idcombo' or $type eq 'hidcombo' or $type eq 'combo') {
427     defined $args{'combo'} or
428     die "widget $widget: mandatory argument 'combo' not defined";
429     }
430     if($type eq 'hidcombo' or $type eq 'hidisearch') {
431     my $r = $args{'ref'};
432     defined $r or
433     die "widget $widget: mandatory argument 'ref' not defined";
434     defined $g{db_tables}{$r} or
435     die "widget $widget: no such table: $r";
436     defined $g{db_fields}{$r}{"${r}_hid"} or
437     die "widget $widget: table $r has no HID";
438     }
439    
440     return ($type, \%args);
441     }
442    
443     sub DB_ReadFields($$$)
444     {
445     my ($dbh, $database, $tables) = @_;
446     my ($query, $sth, $data);
447     my %fields = ();
448    
449     # fields
450     $query = <<'END';
451     SELECT a.attname, t.typname, a.attnum, a.atthasdef, a.atttypmod
452     FROM pg_class c, pg_attribute a, pg_type t
453     WHERE c.relname = ? AND a.attnum > 0
454     AND a.attrelid = c.oid AND a.atttypid = t.oid
455     AND a.attname != ('........pg.dropped.' || a.attnum || '........')
456     ORDER BY a.attnum
457     END
458     $sth = $dbh->prepare($query);
459     for my $table (keys %$tables) {
460     $sth->execute($table) or die $sth->errstr;
461     my $order = 1;
462     while ($data = $sth->fetchrow_arrayref()) {
463     if($data->[0] eq 'meta_sort') {
464     $tables->{$table}{meta_sort}=1;
465     }
466     else {
467     $fields{$table}{$data->[0]} = {
468     field => $data->[0],
469     order => $order++,
470     type => $data->[1],
471     attnum => $data->[2],
472     atthasdef => $data->[3],
473     atttypmod => $data->[4]
474     };
475     }
476     }
477     }
478     $sth->finish;
479    
480     my %field_descs = ();
481    
482     # read field comments as descriptions
483     if($database->{version} >= 7.2) {
484     $query = <<'END';
485     SELECT a.attname, col_description(a.attrelid, a.attnum)
486     FROM pg_class c, pg_attribute a
487     WHERE c.relname = ? AND a.attnum > 0
488     AND a.attrelid = c.oid
489     AND a.attname != ('........pg.dropped.' || a.attnum || '........')
490     END
491     }
492     else {
493     $query = <<'END';
494     SELECT a.attname, d.description
495     FROM pg_class c, pg_attribute a, pg_description d
496     WHERE c.relname = ? AND a.attnum > 0
497     AND a.attrelid = c.oid
498     AND a.oid = d.objoid
499     AND a.attname != ('........pg.dropped.' || a.attnum || '........')
500     END
501     }
502    
503     $sth = $dbh->prepare($query);
504     for my $table (keys %$tables) {
505     $sth->execute($table) or die $sth->errstr;
506     while ($data = $sth->fetchrow_arrayref()) {
507     defined $data->[1] and $data->[1] !~ /^\s*$/ or next;
508     $fields{$table}{$data->[0]}{desc}=$data->[1];
509     $field_descs{$data->[0]} = $data->[1];
510     }
511     }
512     $sth->finish;
513    
514     # set not-defined field descriptions
515     for my $table (keys %$tables) {
516     for my $field (keys %{$fields{$table}}) {
517     my $f = $fields{$table}{$field};
518     if(not defined $f->{desc}) {
519     if(defined $field_descs{$field}) {
520     $f->{desc} = $field_descs{$field};
521     }
522     else {
523     $f->{desc} = $field;
524     }
525     }
526     }
527     }
528    
529     # defaults
530     $query = <<'END';
531     SELECT d.adsrc FROM pg_attrdef d, pg_class c WHERE
532     c.relname = ? AND c.oid = d.adrelid AND d.adnum = ?;
533     END
534     $sth = $dbh->prepare($query) or die $dbh->errstr;
535     for my $table (keys %$tables) {
536     for my $field (keys %{$fields{$table}}) {
537     if(! $fields{$table}{$field}{atthasdef}) { next; }
538     $sth->execute($table, $fields{$table}{$field}{attnum}) or die $sth->errstr;
539     my $d = $sth->fetchrow_arrayref();
540     $fields{$table}{$field}{default} = $d->[0];
541     $sth->finish;
542     }
543     }
544    
545     # meta fields
546     my %meta_fields = ();
547     $query = <<'END';
548     SELECT meta_fields_table, meta_fields_field, meta_fields_attribute,
549     meta_fields_value FROM meta_fields
550     END
551     $sth = $dbh->prepare($query) or die $dbh->errstr;
552     $sth->execute() or die $sth->errstr;
553     while ($data = $sth->fetchrow_arrayref()) {
554     $meta_fields{lc($data->[0])}{lc($data->[1])}{lc($data->[2])} =
555     $data->[3];
556     }
557     $sth->finish;
558    
559     # foreign-key constraints (REFERENCES)
560     $query = <<'END';
561     SELECT tgargs from pg_trigger, pg_proc where pg_trigger.tgfoid=pg_proc.oid AND pg_trigger.tgname
562     LIKE 'RI_ConstraintTrigger%' AND pg_proc.proname = 'RI_FKey_check_ins'
563     END
564     $sth = $dbh->prepare($query) or die $dbh->errstr;
565     $sth->execute() or die $sth->errstr;
566     while ($data = $sth->fetchrow_arrayref()) {
567     my @d = split(/(?:\000|\\000)/,$$data[0]); # DBD::Pg 0.95: \\000, DBD::Pg 0.98: \000
568     $meta_fields{$d[1]}{$d[4]}{reference} = $d[2];
569     }
570     $sth->finish;
571    
572     # if there is a HID field, then hide the ID field
573     for my $view (keys %$tables) {
574     my $table = $view; $table =~ /^(.*)_list$/ and $table = $1;
575     if(defined $fields{$view}{"${table}_hid"} and
576     defined $fields{$view}{"${table}_id"})
577     {
578     $fields{$view}{"${table}_id"}{hide_list}=1;
579     }
580     }
581    
582     # go through every table and field and fill-in:
583     # - table information in reference fields
584     # - meta information from meta_fields
585     # - widget from type (if not specified)
586     table: for my $table (keys %$tables) {
587     field: for my $field (keys %{$fields{$table}}) {
588     my $f = $fields{$table}{$field};
589     my $m = undef;
590     if(defined $meta_fields{$table}) {
591     $m = $meta_fields{$table}{$field};
592     }
593     if(defined $m) {
594     $f->{widget} = $m->{widget};
595     $f->{reference} = $m->{reference};
596     $f->{copy} = $m->{copy};
597     $f->{sortfunc} = $m->{sortfunc};
598     $f->{markup} = $m->{markup};
599     $f->{align} = $m->{align};
600     $f->{hide_list} = $m->{hide_list};
601     }
602     #if(! defined $f->{widget}) {
603     $f->{widget} = DB_Widget(\%fields, $f);
604     #}
605     }
606     }
607    
608     return \%fields;
609     }
610    
611     sub DB_Connect($$)
612     {
613     my $user = shift;
614     my $pass = shift;
615     my $dbh;
616     if($dbh = DBI->connect_cached("$g{conf}{db_datasource}", $user, $pass)) {
617     if(not defined $g{db_meta_loaded}) {
618     DB_Init($user, $pass) or return undef;
619     $g{db_meta_loaded} = 1;
620     }
621     return $dbh;
622     }
623     return undef;
624     }
625    
626     sub DB_GetDefault($$$)
627     {
628     my $dbh = shift;
629     my $table = shift;
630     my $field = shift;
631    
632     my $query = $g{db_fields}{$table}{$field}{default};
633     return undef unless defined $query;
634    
635     $query = "SELECT ".$query;
636     my $sth = $dbh->prepare_cached($query) or die $dbh->errstr;
637     #print "<!-- Executing: $query -->\n";
638     $sth->execute() or die $sth->errstr;
639     my $d = $sth->fetchrow_arrayref();
640     my $default = $d->[0];
641     $sth->finish;
642    
643     return $default;
644     }
645    
646     sub DB_DB2HTML($$)
647     {
648     my $str = shift;
649     my $type = shift;
650    
651     # undef -> ''
652     $str = '' unless defined $str;
653    
654     # trim space
655     $str =~ s/^\s+//;
656     $str =~ s/\s+$//;
657    
658     if($type eq 'bool') {
659     $str = ($str ? 'yes' : 'no');
660     }
661     if($type eq 'text' and $str !~ /<[^>]+>/) { #make sure the text does not contain html
662     $str =~ s/\n/<BR>/g;
663     }
664     if($str eq '') {
665     $str = '&nbsp;';
666     }
667    
668     return $str;
669     }
670    
671     # this is merely an envelope for DB_FetchList()
672     sub DB_GetNumRecords($$)
673     {
674     my $s = shift;
675     my $spec = shift;
676    
677     $spec->{countrows} = 1;
678     return DB_FetchList($s, $spec);
679     }
680    
681     sub DB_FetchListSelect($$)
682     {
683     my $dbh = shift;
684     my $spec = shift;
685     my $v = $spec->{view};
686    
687     # does the view/table exist?
688     defined $g{db_fields_list}{$v} or die "no such table: $v\n";
689    
690     # go through fields and build field list for SELECT (...)
691     my @fields = @{$g{db_fields_list}{$v}};
692     my @select_fields;
693     for my $f (@fields) {
694     if($g{db_fields}{$v}{$f}{type} eq 'bytea') {
695     push @select_fields, "substring($f,1,position(' '::bytea in $f)-1)";
696     }
697     else {
698     push @select_fields, $f;
699     }
700     }
701    
702     my @query_parameters = ();
703    
704     my $query = "SELECT ";
705     $query .= $spec->{countrows} ? "COUNT(*)" : join(', ',@select_fields);
706     $query .= " FROM $v";
707     my $searching=0;
708     if(defined $spec->{search_field} and defined $spec->{search_value}
709     and $spec->{search_field} ne '' and $spec->{search_value} ne '')
710     {
711     my $type = $g{db_fields}{$v}{$spec->{search_field}}{type};
712    
713    
714     if($type eq 'date') {
715     $query .= " WHERE $spec->{search_field} = ? ";
716     push @query_parameters, "$spec->{search_value}";
717     }
718     elsif($type eq 'bool') {
719     $query .= " WHERE $spec->{search_field} = ? ";
720     push @query_parameters, "$spec->{search_value}";
721     }
722     elsif($type eq 'bytea') {
723     $query .= " WHERE position(?::bytea in $spec->{search_field}) != 0";
724     push @query_parameters, "$spec->{search_value}";
725     }
726     else {
727     $query .= " WHERE $spec->{search_field} ~* ? ";
728     push @query_parameters, ".*$spec->{search_value}.*";
729     }
730     $searching=1;
731     }
732     if(defined $spec->{filter_field} and defined $spec->{filter_value}) {
733     if($searching) {
734     $query .= ' AND';
735     }
736     else {
737     $query .= ' WHERE';
738     }
739     $query .= " $spec->{filter_field} = ? ";
740     push @query_parameters, "$spec->{filter_value}";
741     }
742     unless ($spec->{countrows}) {
743     if (defined $spec->{orderby} and $spec->{orderby} ne '') {
744     if (defined $g{db_fields}{$v}{$spec->{orderby}}{sortfunc}) {
745     my $f = $g{db_fields}{$v}{$spec->{orderby}}{sortfunc};
746     $query .= " ORDER BY $f($spec->{orderby})";
747     } else {
748     $query .= " ORDER BY $spec->{orderby}";
749     }
750     if ($spec->{descending}) {
751     $query .= " DESC";
752     }
753     if (defined $g{db_tables}{$v}{meta_sort}) {
754     $query .= ", $v.meta_sort";
755     }
756     else {
757     # if sorting on a non unique field,
758     # then the order of the record is not
759     # guaranteed -> this can be confusing
760     # while scrolling.
761     # try to put order by sorting additionally
762     # with first field, assumed to be the ID
763     $query .= ", $fields[0]";
764     }
765     } elsif (defined $g{db_tables}{$v}{meta_sort}) {
766     $query .= " ORDER BY $v.meta_sort";
767     }
768     if (defined $spec->{limit} and $spec->{limit} != -1 and !$spec->{export})
769     {
770     $query .= " LIMIT $spec->{limit}";
771     }
772     if (defined $spec->{offset} and !$spec->{countrows}) {
773     $query .= " OFFSET $spec->{offset}";
774     }
775     }
776    
777    
778     # print "\n<!-- $query -->\n" unless $spec->{export};
779     # this is kind of useless now that query's are made with the ? placeholders.
780    
781     my $sth = $dbh->prepare_cached($query) or die $dbh->errstr;
782    
783     for(1..scalar(@query_parameters)){
784     #count from 1 to number_of_parameters including.
785     #sql parameters start at 1.
786     $sth->bind_param($_,shift @query_parameters);
787     }
788    
789     $sth->execute() or die $sth->errstr . " ($query)";
790     return (\@fields, $sth);
791     }
792    
793     sub DB_FetchList($$)
794     {
795     my $s = shift;
796     my $spec = shift;
797    
798     my $dbh = $s->{dbh};
799     my $user = $s->{user};
800     my $v = $spec->{view};
801    
802     # fetch one row more than necessary, so that we
803     # can find out when we are at the end (skip if DB_GetNumRecords)
804     $spec->{limit}++ unless $spec->{countrows};
805    
806     my ($fields, $sth) = DB_FetchListSelect($dbh, $spec);
807    
808     # if this is actually a call to DB_GetNumRecords()
809     if($spec->{countrows}) {
810     my $data = $sth->fetchrow_arrayref();
811     $sth->finish or die $sth->errstr;
812     return $data->[0];
813     }
814    
815     # the idea of the %list hash, which then gets passed to GUI_ListTable
816     # is that it is a self-contained description of the data. It shouldn't
817     # be necessary to go look at db_tables and db_fields to figure out how
818     # to display the data, so we need to provide all the required
819     # information here
820     my %list = (
821     spec => $spec,
822     data => [],
823     fields => $fields,
824     acl => defined $g{db_tables}{$spec->{table}}{acls}{$user} ?
825     $g{db_tables}{$spec->{table}}{acls}{$user} : ''
826     );
827     my $col = 0;
828     my @columns;
829     for my $f (@{$list{fields}}) {
830     $columns[$col] = {
831     field => $f,
832     desc => $g{db_fields}{$v}{$f}{desc},
833     align => $g{db_fields}{$v}{$f}{align},
834     hide_list => $g{db_fields}{$v}{$f}{hide_list},
835     markup => $g{db_fields}{$v}{$f}{markup},
836     type => $g{db_fields}{$v}{$f}{type},
837     };
838     $col++;
839     }
840     $list{columns} = \@columns;
841    
842     # fetch the data
843     while(my $data = $sth->fetchrow_arrayref()) {
844     my $col;
845     my @row;
846     for($col=0; $col<=$#$data; $col++) {
847     push @row, $spec->{export} ? $data->[$col] :
848     DB_DB2HTML($data->[$col], $columns[$col]{type});
849     }
850    
851     push @{$list{data}}, [ $data->[0], \@row ];
852     }
853     die $sth->errstr if $sth->err;
854    
855     # are we at the end?
856     if(scalar @{$list{data}} != $spec->{limit}) {
857     $list{end} = 1
858     }
859     else {
860     $list{end} = 0;
861     pop @{$list{data}}; # we did get one more than requested
862     }
863     # decrement temporarily incremented LIMIT count
864     $spec->{limit}--;
865    
866     return \%list;
867     }
868    
869     sub DB_GetRecord($$$$)
870     {
871     my $dbh = shift;
872     my $table = shift;
873     my $id = shift;
874     my $record = shift;
875    
876     my @fields_list = @{$g{db_fields_list}{$table}};
877     #update the query to prevent listing binary data
878     my @select_fields = @fields_list;
879     for(@select_fields){
880     if($g{db_fields}{$table}{$_}{type} eq 'bytea'){
881     $_ = "substring($_,1,position(' '::bytea in $_)-1)";
882     }
883     }
884    
885     # fetch raw data
886     my $data;
887     my $query = "SELECT ";
888     $query .= join(', ',@select_fields); # @{$g{db_fields_list}{$table}});
889     $query .= " FROM $table WHERE ${table}_id = $id";
890     my $sth;
891     $sth = $dbh->prepare_cached($query) or die $dbh->errstr;
892     $sth->execute() or die $sth->errstr;
893     $data = $sth->fetchrow_arrayref() or
894     die ($sth->err ? $sth->errstr : "Record not found ($query)\n");
895    
896     # transorm raw data into record
897     my $i=0;
898     for(@fields_list) {
899     $record->{$_} = $data->[$i];
900     $i++;
901     }
902    
903     return 1;
904     }
905    
906     sub DB_ID2HID($$$)
907     {
908     my $dbh = shift;
909     my $table = shift;
910     my $id = shift;
911    
912     return unless defined $id and $id ne '';
913     my $q = "SELECT ${table}_hid FROM ${table} WHERE ${table}_id = '$id'";
914     my $sth = $dbh->prepare_cached($q) or die $dbh->errstr;
915     $sth->execute or die $sth->errstr;
916     my $d = $sth->fetchrow_arrayref();
917     die $sth->errstr if $sth->err;
918    
919     return $d->[0];
920     }
921    
922     sub DB_HID2ID($$$)
923     {
924     my $dbh = shift;
925     my $table = shift;
926     my $hid = shift;
927    
928     return unless defined $hid and $hid ne '';
929     my $q = "SELECT ${table}_id FROM ${table} WHERE ${table}_hid = ?";
930     my $sth = $dbh->prepare_cached($q) or die $dbh->errstr;
931     $sth->execute($hid) or die $sth->errstr;
932     my $d = $sth->fetchrow_arrayref();
933     die $sth->errstr if $sth->err;
934    
935     return $d->[0];
936     }
937    
938     sub DB_PrepareData($$)
939     {
940     $_ = shift;
941     $_ = '' unless defined $_;
942     my $type = shift;
943     s/^\s+//;
944     s/\s+$//;
945    
946     # quoting for the SQL statements
947     # obsolete since migration to placeholder querys
948     # insert ... values(?,?) etc.
949    
950    
951     #s/\\/\\\\/g;
952     #s/'/\\'/g;
953    
954     if($type eq 'bool') {
955     $_ = ($_ ? '1' : '0');
956     }
957    
958     # this is a hack. It should be implemented in GUI.pm or
959     # (better) with a widget-type
960     if($type eq 'numeric') {
961     if(/^(\d*):(\d+)$/) {
962     my $hours = $1 or 0;
963     my $mins = $2;
964     $_ = $hours+$mins/60;
965     }
966     }
967    
968     if($_ eq '') {
969     $_ = undef;
970     }
971    
972     return $_;
973     }
974    
975     sub DB_Record2DB($$$$)
976     {
977     my $dbh = shift;
978     my $table = shift;
979     my $record = shift;
980     my $dbdata = shift;
981    
982     my $fields = $g{db_fields}{$table};
983     my @fields_list = @{$g{db_fields_list}{$table}};
984    
985     my $f;
986     for $f (@fields_list) {
987     my $type = $fields->{$f}{type};
988     my $data = $record->{$f};
989    
990     $data = DB_PrepareData($data, $type);
991    
992     $dbdata->{$f} = $data;
993     }
994     }
995    
996     sub DB_ExecQuery($$$$$)
997     {
998     my $dbh = shift;
999     my $table = shift;
1000     my $query = shift;
1001     my $data = shift;
1002     my $fields = shift;
1003    
1004     my %datatypes = ();
1005     for(@$fields){
1006     $datatypes{$_} = $g{db_fields}{$table}{$_}{type};
1007     }
1008    
1009     #print "<!-- Executing: $query -->\n";
1010    
1011     my $sth = $dbh->prepare($query) or die $dbh->errstr;
1012    
1013     my $paramnumber = 1;
1014     for(@$fields){
1015     my $type = $datatypes{$_};
1016     my $data = $data->{$_};
1017     if($type eq "bytea") {
1018     #note the reference to the large blob
1019     $sth->bind_param($paramnumber,$$data,{ pg_type => DBD::Pg::PG_BYTEA });
1020     }
1021     else {
1022     $sth->bind_param($paramnumber,$data);
1023     }
1024     $paramnumber++;
1025     }
1026     my $res = $sth->execute() or do {
1027     # report nicely the error
1028     $g{db_error}=$sth->errstr; return undef;
1029     };
1030     if($res ne 1 and $res ne '0E0') {
1031     die "Number of rows affected is not 1! ($res)";
1032     }
1033     return 1;
1034     }
1035    
1036     sub DB_AddRecord($$$)
1037     {
1038     my $dbh = shift;
1039     my $table = shift;
1040     my $record = shift;
1041    
1042     my $fields = $g{db_fields}{$table};
1043     my @fields_list = grep !/${table}_id/, @{$g{db_fields_list}{$table}};
1044    
1045     # filter-out readonly fields
1046     @fields_list = grep { not defined $g{db_fields}{$table}{$_}{widget} or $g{db_fields}{$table}{$_}{widget} ne 'readonly' } @fields_list;
1047    
1048     my %dbdata = ();
1049     DB_Record2DB($dbh, $table, $record, \%dbdata);
1050    
1051     my $query = "INSERT INTO $table (";
1052     $query .= join(', ',@fields_list);
1053     $query .= ") VALUES (";
1054     my $first = 1;
1055     for(@fields_list) {
1056     if($first) {
1057     $first = 0;
1058     }
1059     else {
1060     $query .= ', ';
1061     }
1062     $query .= '?'
1063     }
1064     $query .= ")";
1065     return DB_ExecQuery($dbh,$table,$query,\%dbdata,\@fields_list);
1066     }
1067    
1068     sub DB_UpdateRecord($$$)
1069     {
1070     my $dbh = shift;
1071     my $table = shift;
1072     my $record = shift;
1073    
1074     my $fields = $g{db_fields}{$table};
1075     my @fields_list = @{$g{db_fields_list}{$table}};
1076    
1077     # filter-out readonly fields
1078     @fields_list = grep { $g{db_fields}{$table}{$_}{widget} ne 'readonly' } @fields_list;
1079    
1080     # filter-out bytea fields that have value=undef
1081     # these should keep the value that is now in the database.
1082     @fields_list = grep { defined($record->{$_}) or $g{db_fields}{$table}{$_}{type} ne 'bytea' } @fields_list;
1083    
1084     my %dbdata = ();
1085     DB_Record2DB($dbh, $table, $record, \%dbdata);
1086    
1087    
1088     my @updates;
1089     my $query = "UPDATE $table SET ";
1090     my @updatefields;
1091     for(@fields_list) {
1092     if($_ eq "id") { next; }
1093     if($_ eq "${table}_id") { next; }
1094     push @updates,"$_ = ?";
1095     push @updatefields,$_;
1096     }
1097     $query .= join(', ',@updates);
1098     $query .= " WHERE ${table}_id = $record->{id}";
1099    
1100     return DB_ExecQuery($dbh,$table,$query,\%dbdata,\@updatefields);
1101     }
1102    
1103     sub DB_GetCombo($$$)
1104     {
1105     my $dbh = shift;
1106     my $combo_view = shift;
1107     my $combo_data = shift;
1108    
1109     my $query = "SELECT id, text FROM $combo_view";
1110     if(defined $g{db_tables}{$combo_view}{meta_sort}) {
1111     $query .= " ORDER BY meta_sort";
1112     }
1113     else {
1114     $query .= " ORDER BY text";
1115     }
1116     my $sth = $dbh->prepare_cached($query) or die $dbh->errstr;
1117     $sth->execute() or die $sth->errstr;
1118     my $data;
1119     while($data = $sth->fetchrow_arrayref()) {
1120     $data->[0]='' unless defined $data->[0];
1121     $data->[1]='' unless defined $data->[1];
1122     push @$combo_data, [$data->[0], $data->[1]];
1123     }
1124     die $sth->errstr if $sth->err;
1125    
1126     return 1;
1127     }
1128    
1129     sub DB_DeleteRecord($$$)
1130     {
1131     my $dbh = shift;
1132     my $table = shift;
1133     my $id = shift;
1134    
1135     my $query = "DELETE FROM $table WHERE ${table}_id = $id";
1136    
1137     #print "<!-- Executing: $query -->\n";
1138     my $sth = $dbh->prepare($query) or die $dbh->errstr;
1139     $sth->execute() or do {
1140     # report nicely the error
1141     $g{db_error}=$sth->errstr; return undef;
1142     };
1143    
1144     return 1;
1145     }
1146    
1147     sub DB_GetBlobName($$$$)
1148     {
1149     my $dbh = shift;
1150     my $table = shift;
1151     my $field = shift;
1152     my $id = shift;
1153    
1154     my $idcolumn = "${table}_id";
1155     if($table =~ /\w+_list/){
1156     #tables that end with _list are actualy views and have their
1157     # id column as the first column of the view
1158     $idcolumn = $g{db_fields_list}{$table}[0];
1159     }
1160    
1161     my $query = "Select substring($field,1,position(' '::bytea in $field)-1) from $table where $idcolumn=$id";
1162     my $sth = $dbh->prepare($query);
1163     $sth->execute() or return undef;
1164     my $data = $sth->fetchrow_arrayref() or return undef;
1165     return $data->[0];
1166     }
1167    
1168     sub DB_GetBlobType($$$$)
1169     {
1170     my $dbh = shift;
1171     my $table = shift;
1172     my $field = shift;
1173     my $id = shift;
1174    
1175     my $idcolumn = "${table}_id";
1176     if($table =~ /\w+_list/){
1177     #tables that end with _list are actualy views and have their
1178     # id column as the first column of the view
1179     $idcolumn = $g{db_fields_list}{$table}[0];
1180     }
1181    
1182     my $query = "Select substring($field,position(' '::bytea in $field)+1,position('#'::bytea in $field)-(position(' '::bytea in $field)+1)) from $table where $idcolumn=$id";
1183     my $sth = $dbh->prepare($query);
1184     $sth->execute() or return undef;
1185     my $data = $sth->fetchrow_arrayref() or return undef;
1186     return $data->[0];
1187     }
1188    
1189     sub DB_DumpBlob($$$$)
1190     {
1191     my $dbh = shift;
1192     my $table = shift;
1193     my $field = shift;
1194     my $id = shift;
1195    
1196     my $idcolumn = "${table}_id";
1197     if($table =~ /\w+_list/){
1198     #tables that end with _list are actualy views and have their
1199     # id column as the first column of the view.
1200     $idcolumn = $g{db_fields_list}{$table}[0];
1201     }
1202    
1203     my $query = "Select position('#'::bytea in $field)+1,octet_length($field) from $table where $idcolumn=$id";
1204     my $sth = $dbh->prepare($query);
1205     $sth->execute() or return -1;
1206     my $data = $sth->fetchrow_arrayref() or return -1;
1207     my $startpos = $data->[0] || 0;
1208     my $strlength = $data->[1] || 0;
1209     $sth->finish();
1210     my $endpos = $strlength-($startpos-1);
1211     my $dumpquery = "Select substring($field,?,?) from $table where $idcolumn=$id";
1212     my $dumpsth = $dbh->prepare($dumpquery);
1213     my $blobdata;
1214     $dumpsth->execute($startpos,$endpos) or return -1;
1215     $blobdata = $dumpsth->fetchrow_arrayref() or return -1;
1216     # I know it is not nice to do the print here but I don't want to make the memory footprint
1217     # to large so returning the blob to a GUI routine is not possible.
1218     print $blobdata->[0];
1219     return 1;
1220     }
1221    
1222     sub DB_RawField($$$$)
1223     {
1224     my $dbh = shift;
1225     my $table = shift;
1226     my $field = shift;
1227     my $id = shift;
1228    
1229     my $query = "Select $field from $table where ${table}_id = $id";
1230     # print STDERR $query."\n";
1231     my $sth = $dbh->prepare($query);
1232     $sth->execute() or return undef;
1233     my $data = $sth->fetchrow_arrayref() or return undef;
1234     return $data->[0];
1235     }
1236    
1237     sub DB_DumpTable($$$)
1238     {
1239     my $dbh = shift;
1240     my $table = shift;
1241     my $view = defined $g{db_tables}{"${table}_list"} ?
1242     "${table}_list" : $table;
1243     my $atribs = shift;
1244    
1245     my @fields = @{$g{db_fields_list}{$view}};
1246     # update the query to prevent listing binary data
1247     my @select_fields = @fields;
1248     for(@select_fields){
1249     if($g{db_fields}{$view}{$_}{type} eq 'bytea'){
1250     $_ = "substring($_,1,position(' '::bytea in $_)-1)";
1251     }
1252     }
1253    
1254     my $query = "SELECT ";
1255     $query .= join(', ',@select_fields);
1256     $query .= " FROM $view";
1257    
1258     # fix this for placeholders
1259    
1260     my $first = 1;
1261     for my $field (keys(%$atribs)){
1262     if($first){
1263     $query .= " where ";
1264     }else{
1265     $query .= " and ";
1266     }
1267     my $value = $atribs->{$field};
1268     my $type = $g{db_fields}{$view}{$field}{type};
1269     if($type eq 'date') {
1270     $query .= " $field = '$value'";
1271     }
1272     elsif($type eq 'bool') {
1273     $query .= " $field = '$value'";
1274     }
1275     else {
1276     $query .= " $field ~* '.*$value.*'";
1277     }
1278     }
1279    
1280     my $sth = $dbh->prepare($query) or return undef;
1281     $sth->execute() or return undef;
1282    
1283     my (@row, $data);
1284    
1285     $data=$sth->rows."\n";
1286    
1287     $first = 1;
1288     my $numcolumns = scalar @select_fields;
1289     while(@row = $sth->fetchrow_array()) {
1290     $first = 1;
1291     for (0..$numcolumns-1){
1292     my $field=$row[$_];
1293     if(!$field||$field eq ""){
1294     $field = " ";
1295     }
1296    
1297     if(not $first){
1298     $data.="\t";
1299     }
1300     $first = 0;
1301     $field =~ s/\t/\&\#09\;/gm;
1302     $field =~ s/\n/\&\#10\;/gm;
1303     $field =~ s/[\r\f]//gm;
1304    
1305     $data .= $field;
1306     }
1307     $data .= "\n";
1308     }
1309     $sth->finish();
1310     if(length($data)>20000){
1311     $data = "Resultset exeeds desirable size.\n";
1312     }
1313     return $data;
1314     }
1315    
1316     1;

  ViewVC Help
Powered by ViewVC 1.1.26