File Coverage

blib/lib/CAD/Drawing/IO/PgDB.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package CAD::Drawing::IO::PgDB;
2             our $VERSION = '0.03';
3              
4 1     1   35294 use CAD::Drawing;
  0            
  0            
5             use CAD::Drawing::Defined;
6              
7             use DBI;
8             use Storable qw(freeze);
9             use Digest::MD5 qw(md5);
10              
11             use strict;
12             use Carp;
13              
14             ########################################################################
15             =pod
16              
17             =head1 NAME
18              
19             CAD::Drawing::IO::PgDB - PostgreSQL save / load methods
20              
21             =head1 NOTICE
22              
23             This module is considered pre-ALPHA and under-documented. Its use is
24             strongly discouraged except under experimental conditions. Particularly
25             susceptible to change will be the table structure of the database, which
26             currently does not yet even have any auto-create method.
27              
28             =head1 AUTHOR
29              
30             Eric L. Wilhelm
31              
32             http://scratchcomputing.com
33              
34             =head1 COPYRIGHT
35              
36             This module is copyright (C) 2004-2006 by Eric L. Wilhelm. Portions
37             copyright (C) 2003 by Eric L. Wilhelm and A. Zahner Co.
38              
39             =head1 LICENSE
40              
41             This module is distributed under the same terms as Perl. See the Perl
42             source package for details.
43              
44             You may use this software under one of the following licenses:
45              
46             (1) GNU General Public License
47             (found at http://www.gnu.org/copyleft/gpl.html)
48             (2) Artistic License
49             (found at http://www.perl.com/pub/language/misc/Artistic.html)
50              
51             =head1 NO WARRANTY
52              
53             This software is distributed with ABSOLUTELY NO WARRANTY. The author,
54             his former employer, and any other contributors will in no way be held
55             liable for any loss or damages resulting from its use.
56              
57             =head1 Modifications
58              
59             The source code of this module is made freely available and
60             distributable under the GPL or Artistic License. Modifications to and
61             use of this software must adhere to one of these licenses. Changes to
62             the code should be noted as such and this notification (as well as the
63             above copyright information) must remain intact on all copies of the
64             code.
65              
66             Additionally, while the author is actively developing this code,
67             notification of any intended changes or extensions would be most helpful
68             in avoiding repeated work for all parties involved. Please contact the
69             author with any such development plans.
70              
71             =head1 SEE ALSO
72              
73             CAD::Drawing
74             DBI
75             DBD::Pg
76              
77             =cut
78             ########################################################################
79              
80             =head1 Requisite Plug-in Functions
81              
82             See CAD::Drawing::IO for a description of the plug-in architecture.
83              
84             =cut
85             ########################################################################
86             # the following are required to be a disc I/O plugin:
87             our $can_save_type = "pgdb";
88             our $can_load_type = $can_save_type;
89             our $is_inherited = 1;
90              
91             =head2 check_type
92              
93             Returns true if $type is "circ" or $filename is a directory containing a
94             ".circ" file.
95              
96             $fact = check_type($filename, $type);
97              
98             =cut
99             sub check_type {
100             my ($filename, $type) = @_;
101             if(defined($type)) {
102             ($type eq "pgdb") && return("pgdb");
103             return();
104             }
105             if($filename =~ m/^dbi:/) {
106             return("pgdb");
107             }
108             return();
109             } # end subroutine check_type definition
110             ########################################################################
111              
112             =head1 Back-End Input and output methods
113              
114             The functions load() and save() are responsible for determining the
115             filetype (with forced types available via $opt->{type}.) These then
116             call the appropriate load or save functions.
117              
118             =cut
119             ########################################################################
120              
121             =head2 load
122              
123             Loads a CAD::Drawing object from an SQL database. $spec should be of
124             the form required by the database driver.
125              
126             $opts->{auth} = ["username", "password"] may be required to create a
127             connection.
128              
129             $drw->load($spec, $opts);
130              
131             =cut
132             sub load {
133             my $self = shift;
134             my ($spec, $options) = @_;
135             my %opts = parse_options($spec, $options);
136             my $dbh = $opts{handle};
137             $dbh || (
138             $dbh = DBI->connect(
139             $opts{spec}, $opts{username}, $opts{password},
140             ) or croak("connection failed\n")
141             );
142             my %have = map( {$_ => 1} $dbh->tables);
143             $have{drawing} or croak("$spec has no drawing table");
144             $have{layer} or croak("$spec has no layer table");
145             my $drawing = $opts{drawing};
146             # my $col = $dbh->selectcol_arrayref(
147             # "select layer_name from layer where dwg_name=?",
148             # {}, $drawing) or
149             # croak "get layers failed";
150             # my @layers = @$col;
151             # faster replacement method
152             my $got = $dbh->selectall_arrayref(
153             "select layer_name, layer_id FROM layer " .
154             "WHERE dwg_name = ?", {}, $drawing) or
155             croak "get layers failed";
156             my %layer_id = map({$_->[0] => $_->[1]} @$got);
157             my @layers = keys(%layer_id);
158             @layers or croak "no layers for $drawing";
159             # print "gots: @$got\n";exit;
160              
161            
162             # print "got layers:\n\t", join("\n\t", @layers), "\n";
163             my($s, $n) = check_select(\%opts);
164             my %want = map({$_ => 1} keys(%call_syntax));
165             if($s->{t}) {
166             %want = %{$s->{t}};
167             }
168             if($n->{t}) {
169             foreach my $type (keys(%{$n->{t}})) {
170             $want{$type} = 0;
171             }
172             }
173             my $ftchdbg = 0; # for fetch debugs
174             my $stat = $opts{show_stat};
175             if($have{polyline} and $want{plines}) {
176             # new method:
177             my $plines = $dbh->selectall_arrayref(
178             "SELECT " . join(", ",
179             map({"l." . $_}
180             qw(
181             layer_name
182             )
183             ),
184             map({"p." . $_}
185             qw(
186             line_id
187             line_value
188             sclosed
189             color
190             linetype
191             )
192             ),
193             ) . " " . # end join
194             "FROM layer l, polyline p " .
195             "WHERE p.layer_id = l.layer_id " .
196             "AND l.dwg_name = ?",
197             {},
198             $drawing
199             );
200             foreach my $pl (@{$plines}) {
201             my ($l, $id, $lv, $cl, $co, $lt) = @$pl;
202             $s->{l} && ($s->{l}{$l} || next);
203             $n->{l} && ($n->{l}{$l} && next);
204             $s->{c} && ($s->{c}{$co} || next);
205             $n->{c} && ($n->{c}{$co} && next);
206             $stat && print "pline: $l\n";
207             my %plopts = (
208             "closed" => $cl,
209             "color" => $co,
210             "layer" => $l,
211             "linetype" => $lt,
212             "id" => $id,
213             );
214             my @pts = map({[split(/\s*,\s*/, $_)]
215             } split(/\s*:\s*/, $lv)
216             );
217             my $addr = $self->addpolygon(\@pts, \%plopts);
218              
219             } # end foreach $pl
220             } # end if polyline
221             if(($have{inst_point} and $have{data_point}) and $want{points} ) {
222             my $points = $dbh->selectall_arrayref(
223             "SELECT " . join(", ",
224             map({"l." . $_}
225             qw(
226             layer_name
227             )
228             ),
229             "i.match_id",
230             map({"d." . $_ . "_value"} qw(x y z)),
231             map({"i." . $_}
232             qw(
233             color
234             linetype
235             )
236             ),
237             ) . " " . # end join
238             "FROM layer l, inst_point i, data_point d " .
239             "WHERE i.layer_id = l.layer_id ".
240             "AND l.dwg_name = ?".
241             "AND i.point_id = d.point_id",
242             {},
243             $drawing
244             );
245             foreach my $po (@{$points}) {
246             ($stat > 1) && print "point\n";
247             my ($l, $id, $x, $y, $z, $co, $lt) = @$po;
248             $s->{l} && ($s->{l}{$l} || next);
249             $n->{l} && ($n->{l}{$l} && next);
250             $s->{c} && ($s->{c}{$co} || next);
251             $n->{c} && ($n->{c}{$co} && next);
252             my %poopts = (
253             "color" => $co,
254             "layer" => $l,
255             "linetype" => $lt,
256             "id" => $id,
257             );
258             my $addr = $self->addpoint([$x,$y,$z], {%poopts});
259             } # end foreach $po
260             } # end if have points and such
261              
262              
263             unless($opts{handle}) {
264             $dbh->disconnect();
265             }
266              
267             return();
268             ########################################################################
269             # end used code
270            
271             my %sth;
272             foreach my $layer (@layers) {
273             $s->{l} && ($s->{l}{$layer} || next);
274             $n->{l} && ($n->{l}{$layer} && next);
275             $stat && print "$layer\n";
276             if($have{arcs} and $want{arcs}) {
277             # load them
278             $sth{arcs} || ($sth{arcs} =
279             $dbh->prepare(
280             "SELECT " . join(", ",
281             "arc_id",
282             "x_value",
283             "y_value",
284             "z_value",
285             "radius",
286             "stang",
287             "endang",
288             "color",
289             "linetype",
290             ) . " " .
291             "FROM arcs " .
292             "WHERE layer_id = ?")
293             );
294             $ftchdbg && print "layer_id is $layer_id{$layer}\n";
295             my $success = $sth{arcs}->execute($layer_id{$layer});
296             my $arcs = $sth{arcs}->fetchall_arrayref;
297             foreach my $ar (@$arcs) {
298             my ($id, $x, $y, $z, $r, $sa, $ea, $co, $lt) = @$ar;
299             $s->{c} && ($s->{c}{$co} || next);
300             $n->{c} && ($n->{c}{$co} && next);
301             $ftchdbg && print "fetching arc $id\n";
302             ($stat > 1) && print "arc\n";
303             my %aropts = (
304             "color" => $co,
305             "layer" => $layer,
306             "linetype" => $lt,
307             "id" => $id,
308             );
309             my @angs = ($sa, $ea);
310             my $addr = $self->addarc([$x,$y,$z], $r, \@angs, {%aropts});
311             } # end foreach $ar
312             }
313             if($have{circles} and $want{circles}) {
314             # load these
315             $sth{circles} || ($sth{circles} =
316             $dbh->prepare(
317             "SELECT " . join(", ",
318             "circle_id",
319             "x_value",
320             "y_value",
321             "z_value",
322             "radius",
323             "color",
324             "linetype",
325             ) . " " .
326             "FROM circles " .
327             "WHERE layer_id = ?")
328             );
329             $ftchdbg && print "layer_id is $layer_id{$layer}\n";
330             my $success = $sth{circles}->execute($layer_id{$layer});
331             my $circles = $sth{circles}->fetchall_arrayref;
332             foreach my $ci (@$circles) {
333             my($id, $x,$y,$z,$r,$co,$lt) = @$ci;
334             $s->{c} && ($s->{c}{$co} || next);
335             $n->{c} && ($n->{c}{$co} && next);
336             $ftchdbg && print "fetching circle $id\n";
337             ($stat > 1) && print "circle\n";
338             my %ciopts = (
339             "color" => $co,
340             "layer" => $layer,
341             "linetype" => $lt,
342             "id" => $id,
343             );
344             my $addr = $self->addcircle([$x,$y,$z], $r, {%ciopts});
345             } # end foreach $ci
346             } # end if $have{circles}
347             if($have{lines} and $want{lines}) {
348             # load these
349             $sth{lines} || ($sth{lines} =
350             $dbh->prepare(
351             "SELECT " . join(", ",
352             "line_id",
353             "x1_value",
354             "y1_value",
355             "z1_value",
356             "x2_value",
357             "y2_value",
358             "z2_value",
359             "color",
360             "linetype",
361             ) . " " .
362             "FROM lines " .
363             "WHERE layer_id = ?")
364             );
365             $ftchdbg && print "layer_id is $layer_id{$layer}\n";
366             my $success = $sth{lines}->execute($layer_id{$layer});
367             my $lines = $sth{lines}->fetchall_arrayref;
368             foreach my $li (@$lines) {
369             my($id, $x1,$y1,$z1, $x2,$y2,$z2, $co, $lt) = @$li;
370             $s->{c} && ($s->{c}{$co} || next);
371             $n->{c} && ($n->{c}{$co} && next);
372             $ftchdbg && print "fetching line $id\n";
373             ($stat > 1) && print "line\n";
374             my %liopts = (
375             "color" => $co,
376             "layer" => $layer,
377             "linetype" => $lt,
378             "id" => $id,
379             );
380             my @pts = (
381             [$x1, $y1, $z1],
382             [$x2, $y2, $z2]
383             );
384             my $addr = $self->addline(\@pts, {%liopts});
385             } # end foreach $li
386             } # end if $have{lines}
387             if($have{points} and $want{points}) {
388             # load these
389             # FIXME: don't have any of these yet
390             }
391             if($have{polyline} and $want{plines}) {
392             # load these
393             # maybe this is much faster:
394             $sth{plines} || ($sth{plines} =
395             $dbh->prepare(
396             "SELECT " . join(", ",
397             "line_id",
398             "line_value",
399             "sclosed",
400             "color",
401             "linetype"
402             ) . " " .
403             "FROM polyline " .
404             "WHERE layer_id = ? " )
405             );
406             $ftchdbg && print "layer_id is $layer_id{$layer}\n";
407             my $success = $sth{plines}->execute($layer_id{$layer});
408             my $plines = $sth{plines}->fetchall_arrayref;
409             # print "fetching polylines for $layer from $drawing\n";
410             # print "got polylines:\n\t",
411             # join("\n\n\t", map({join(" ", @{$_})} @{$plines})), "\n";
412             foreach my $pl (@{$plines}) {
413             my ($id, $lv, $cl, $co, $lt) = @{$pl};
414             $s->{c} && ($s->{c}{$co} || next);
415             $n->{c} && ($n->{c}{$co} && next);
416             $ftchdbg && print "fetching polyline $id\n";
417             ($stat > 1) && print "polyline\n";
418             # print "closed: $cl\n";
419             my %plopts = (
420             "closed" => $cl,
421             "color" => $co,
422             "layer" => $layer,
423             "linetype" => $lt,
424             "id" => $id,
425             );
426             ($stat == 4) && print "string: $lv\n";
427             my @pts = map({[split(/\s*,\s*/, $_)]
428             } split(/\s*:\s*/, $lv)
429             );
430             ($stat > 2 ) &&
431             print "pts:\n\t",
432             join("\n\t",
433             map({join(",",
434             map({sprintf("%0.2f", $_)} @$_))} @pts
435             )
436             ), "\n";
437             #print "got points:\n\t",
438             # join("\n\t", map({join(",", @{$_})} @pts)), "\n";
439             my $addr = $self->addpolygon(\@pts, {%plopts});
440             } # end foreach $pl
441             } # end if $have{polyline}
442             if($have{"3Dplines"}) {
443            
444             # I'm not sure that we really want to implement these in the
445             # same way as the others. Are 3Dplines really any different
446             # than your run-of-the-mill polylines? If you just load 3D
447             # coordinates into a polyline, it will mostly act like a 3D
448             # polyline until you try to save to and from autocad format.
449             # Given that we have already made the decision to move away
450             # from that, let it be simple everywhere else.
451              
452             } # end if $have{3Dplines}
453             if($have{texts} and $want{texts}) {
454             # load these
455             $sth{texts} || ($sth{texts} =
456             $dbh->prepare(
457             "SELECT " . join(", ",
458             "text_id",
459             "x_value",
460             "y_value",
461             "z_value",
462             "height",
463             "text_string",
464             "color",
465             "linetype",
466             ) . " " .
467             "FROM texts " .
468             "WHERE layer_id = ? ")
469             );
470             $ftchdbg && print "layer_id is $layer_id{$layer}\n";
471             my $success = $sth{texts}->execute($layer_id{$layer});
472             my $texts = $sth{texts}->fetchall_arrayref;
473             foreach my $te (@{$texts}) {
474             my ($id, $x, $y, $z, $h, $str, $co, $lt) = @$te;
475             $s->{c} && ($s->{c}{$co} || next);
476             $n->{c} && ($n->{c}{$co} && next);
477             ($stat > 1) && print "text\n";
478             my %teopts = (
479             "height" => $h,
480             "color" => $co,
481             "layer" => $layer,
482             "linetype" => $lt,
483             "id" => $id,
484             );
485             my $addr = $self->addtext([$x,$y,$z], $str, {%teopts});
486             } # end foreach $te
487             } # end if $have{texts}
488             if(($have{inst_point} and $have{data_point}) and $want{points} ) {
489             # FIXME: I currently just load these as if they were
490             # FIXME: typical points
491             $sth{inst_points} || ($sth{inst_points} =
492             $dbh->prepare(
493             "SELECT " . join(", ",
494             "i.match_id",
495             "d.x_value",
496             "d.y_value",
497             "d.z_value",
498             "i.color",
499             "i.linetype",
500             ) . " " .
501             "FROM inst_point i, data_point d " .
502             "WHERE i.layer_id = ?" .
503             "AND i.point_id = d.point_id")
504             );
505             my $success = $sth{inst_points}->execute($layer_id{$layer});
506             my $points = $sth{inst_points}->fetchall_arrayref;
507             foreach my $po (@{$points}) {
508             ($stat > 1) && print "point\n";
509             my ($id, $x, $y, $z, $co, $lt) = @$po;
510             $s->{c} && ($s->{c}{$co} || next);
511             $n->{c} && ($n->{c}{$co} && next);
512             my %poopts = (
513             "color" => $co,
514             "layer" => $layer,
515             "linetype" => $lt,
516             "id" => $id,
517             );
518             $ftchdbg && print "pointid $id\n";
519             ($stat > 2) && print "point: $x, $y, $z\n";
520             my $addr = $self->addpoint([$x,$y,$z], {%poopts});
521             # print "point: $x,$y,$z\n";
522             } # end foreach $po
523             } # end if $have{points}
524             } # end foreach $layer
525            
526            
527             unless($opts{handle}) {
528             $dbh->disconnect();
529             }
530             } # end subroutine load definition
531             ########################################################################
532              
533             =head2 save
534              
535             $drw->save($spec, $opts);
536              
537             =cut
538             sub save {
539             my $self = shift;
540             my ($spec, $options) = @_;
541             my %opts = parse_options($spec, $options);
542             my $drawing = $opts{drawing};
543             my %dbopts;
544             $opts{dbopts} && (%dbopts = %{$opts{dbopts}});
545             defined($dbopts{AutoCommit}) || ($dbopts{AutoCommit} = 0);
546             my $dbh = DBI->connect(
547             $opts{spec}, $opts{username}, $opts{password},
548             \%dbopts
549             ) or croak("connection failed\n");
550             # FIXME: # we could make the required tables (add this later?)
551             my %have = map( {$_ => 1} $dbh->tables);
552             $have{drawing} or croak("$spec has no drawing table");
553             $have{layer} or croak("$spec has no layer table");
554              
555             # FIXME: we need to support selective saves here?
556              
557             # FIXME:
558             # should also have a way to kill deleted items (would have to get
559             # everything from this database for this drawing, then remove it
560             # (which frees us to always INSERT (but prevents building-up a
561             # drawing from separate processes)
562            
563             # FIXME: should have more info to select drawing name
564             my ($had) = $dbh->selectrow_array(
565             "SELECT dwg_name from drawing where dwg_name = ?",
566             {},
567             $drawing
568             );
569             print "table had: $had\n";
570             if($had) {
571             # FIXME: this is currently pointless
572             my $did = $dbh->do(
573             "UPDATE drawing set dwg_name = ? " .
574             "WHERE dwg_name = ?",
575             {
576             AutoCommit => 1,
577             },
578             $drawing, $drawing
579             );
580             }
581             else {
582             # print "insert forced\n";
583             $dbh->do(
584             "INSERT into drawing(dwg_name) VALUES(?)",
585             {},
586             $drawing
587             ) or croak("cannot make drawing", $dbh->errstr);
588             }
589            
590              
591             # Seems like a better plan to simply use REPLACE, but also offer an
592             # option to delete all existing items first (rather than doing all
593             # of the queries and then a few deletes
594              
595             # This would be fine and dandy except that REPLACE is a proprietary
596             # extension implemented only by mysql
597              
598             my @layers = $self->getLayerList();
599             # print "layers: @layers\n";
600             my $to_save = $self->select_addr($options);
601             # print "not a list: @$to_save\n";
602             my %se_h; # SELECT handles
603             my %up_h; # UPDATE handles
604             my %in_h; # INSERT handles
605             $se_h{layers} = $dbh->prepare(
606             "SELECT layer_id " .
607             "FROM layer " .
608             "WHERE layer_name = ? " .
609             "AND dwg_name = ? "
610             );
611             $in_h{layers} = $dbh->prepare(
612             "INSERT into layer(layer_name, dwg_name) " .
613             "VALUES(?, ?)"
614             );
615             my %tntr = (
616             "arcs" => "arcs",
617             "circles" => "circles",
618             "lines" => "lines",
619             "plines" => "polyline", # FIXME: rename that table!
620             "points" => "points",
621             "texts" => "texts",
622             "images" => "images",
623             );
624              
625             my %del_h;
626             foreach my $type (keys(%tntr)) {
627             $have{$tntr{$type}} || next; # no table for that
628             $del_h{$type} = $dbh->prepare(
629             "DELETE from " . $tntr{$type} . " " .
630             "WHERE layer_id = ?"
631             );
632             }
633             # make it the default behaviour to cleanup first
634             defined($opts{clear_layers}) || ($opts{clear_layers} = 1);
635              
636             # now we either have to have loaded the entire thing or provide
637             # some selective kill methods (ack) because source id will not
638             # match dest id!
639            
640             foreach my $layer (@layers) {
641             # print "working on layer $layer\n";
642             $se_h{layers}->execute($layer, $drawing)
643             or croak("cannot lookup $layer in $drawing\n");
644             my ($layer_id) = $se_h{layers}->fetchrow_array();
645             if(defined($layer_id)) {
646             # print "layer id: $layer_id\n";
647             if($opts{clear_layers}) {
648             # print "clearing layer $layer\n";
649             foreach my $type (keys(%del_h)) {
650             # print "clearing type $type\n";
651             $del_h{$type}->execute($layer_id);
652             # print "affecting ", $del_h{$type}->rows, " rows\n";
653             $del_h{$type}->finish();
654             }
655             }
656             }
657             else {
658             # print "should be making new layer\n";
659             $in_h{layers}->execute($layer, $drawing);
660             # nothing beats maintaining knowledge in 5 places!
661             # FIXME: SQL is primitive?
662             my ($this) = $se_h{layers}->execute($layer, $drawing)
663             or croak("cannot lookup $layer in $drawing\n");
664             # print "this came back as $this\n";
665             ($layer_id) = $se_h{layers}->fetchrow_array();
666             # print "new layer_id: $layer_id\n";
667             }
668             # FIXME: would set layer properties here
669             my %these = sort_addr($layer, $to_save);
670             # FIXME: current assumption is that the tables exist!
671             foreach my $point (@{$these{points}}) {
672             # print "have a point\n";
673             my $obj = $self->getobj($point);
674             # FIXME: this crap has GOT to go elsewhere
675             $se_h{points} || (
676             $se_h{points} =
677             $dbh->prepare(
678             "SELECT point_id " .
679             "FROM points " .
680             "WHERE point_id = ? ".
681             "AND layer_id = ? "
682             )
683             );
684             $in_h{points} || (
685             $in_h{points} =
686             $dbh->prepare(
687             "INSERT into points(" .
688             join(", ",
689             "x_value",
690             "y_value",
691             "z_value",
692             "color",
693             "linetype",
694             "layer_id",
695             ) .
696             ") " .
697             "VALUES(?,?,?, ?,?, ?)"
698             )
699             );
700             $up_h{points} || (
701             $up_h{points} =
702             $dbh->prepare(
703             "UPDATE points set " .
704             join(", ",
705             map({"$_ = ?"}
706             "x_value",
707             "y_value",
708             "z_value",
709             "color",
710             "linetype",
711             )
712             ) .
713             "WHERE layer_id = ? " .
714             "AND point_id = ?"
715             )
716             );
717             my $id = $point->{id};
718             $se_h{points}->execute($id, $layer_id);
719             my ($have_id) = $se_h{points}->fetchrow_array;
720             # FIXME: this will eventually have to change to a name!
721             if(defined($have_id)) {
722             # print "replacing $id\n";
723             # over-write it
724             $up_h{points}->execute(
725             $obj->{pt}[0], $obj->{pt}[1], $obj->{pt}[2],
726             $obj->{color}, $obj->{linetype},
727             $layer_id, $id
728             );
729             }
730             else {
731             # print "new for $id\n";
732             # make a new one
733             $in_h{points}->execute(
734             $obj->{pt}[0], $obj->{pt}[1], $obj->{pt}[2],
735             $obj->{color}, $obj->{linetype},
736             $layer_id
737             );
738             }
739             } # end foreach $point
740             foreach my $line (@{$these{lines}}) {
741             } # end foreach $line
742             foreach my $pline (@{$these{plines}}) {
743             my $obj = $self->getobj($pline);
744             if($opts{update_by} eq "color") {
745             $se_h{plines} || (
746             $se_h{plines} =
747             $dbh->prepare(
748             "SELECT line_id " .
749             "FROM polyline " .
750             "WHERE color = ? " .
751             "AND layer_id = ? "
752             )
753             );
754              
755             }
756             else {
757             $se_h{plines} || (
758             $se_h{plines} =
759             $dbh->prepare(
760             "SELECT line_id " .
761             "FROM polyline " .
762             "WHERE line_id = ? " .
763             "AND layer_id = ? "
764             )
765             );
766             }
767             $in_h{plines} || (
768             $in_h{plines} =
769             $dbh->prepare(
770             "INSERT into polyline(" .
771             join(", ",
772             "line_value", "sclosed",
773             "color", "linetype",
774             "layer_id"
775             ) .
776             ") " .
777             "VALUES(?, ?, ?,?, ?)"
778             )
779             );
780             $up_h{plines} || (
781             $up_h{plines} =
782             $dbh->prepare(
783             "UPDATE polyline set " .
784             join(", ",
785             map({"$_ = ?"}
786             "line_value", "sclosed",
787             "color", "linetype",
788             "layer_id"
789             )
790             ) .
791             "WHERE layer_id = ? " .
792             "AND line_id = ? "
793             )
794             );
795             my $pstring = join(":", map({join(",", @$_)} @{$obj->{pts}}));
796             # print "closed: $obj->{closed}\n";
797             my @tr = ("f", "t");
798             # gives the option to update according to any property
799             my $update_by = $opts{update_by};
800             $update_by || ($update_by = "id");
801             my $update_key = $pline->{$update_by};
802             $se_h{plines}->execute($update_key, $layer_id);
803             my ($have_id) = $se_h{plines}->fetchrow_array;
804             if(defined($have_id)) {
805             my $id = $have_id;
806             $up_h{plines}->execute(
807             $pstring, $tr[$obj->{closed}],
808             $obj->{color}, $obj->{linetype},
809             $layer_id, $id
810             );
811             }
812             else {
813             $in_h{plines}->execute(
814             $pstring, $tr[$obj->{closed}],
815             $obj->{color}, $obj->{linetype},
816             $layer_id
817             );
818             }
819             } # end foreach $pline
820             foreach my $circ (@{$these{circs}}) {
821             } # end foreach $circ
822             foreach my $arc (@{$these{arcs}}) {
823             } # end foreach $arc
824             foreach my $text (@{$these{texts}}) {
825             } # end foreach $text
826             } # end foreach $layer
827             $se_h{layers}->finish();
828             $in_h{layers}->finish();
829             foreach my $type (keys(%call_syntax)) {
830             $se_h{$type} && $se_h{$type}->finish();
831             $in_h{$type} && $in_h{$type}->finish();
832             $up_h{$type} && $up_h{$type}->finish();
833             }
834             unless($dbopts{AutoCommit}) {
835             $dbh->commit or
836             croak("commit failed:\n", $dbh->errstr);
837             }
838             $dbh->disconnect();
839             } # end subroutine save definition
840             ########################################################################
841              
842             =head2 cleardb
843              
844             Deletes the drawing and all of its entities from the database.
845              
846             $drw->cleardb();
847              
848             =cut
849             sub cleardb {
850             my $self = shift;
851             my ($spec, $options) = @_;
852             my %opts = parse_options($spec, $options);
853             my $drawing = $opts{drawing};
854             my %dbopts;
855             $opts{dbopts} && (%dbopts = %{$opts{dbopts}});
856             defined($dbopts{AutoCommit}) || ($dbopts{AutoCommit} = 0);
857             my $dbh = DBI->connect(
858             $opts{spec}, $opts{username}, $opts{password},
859             \%dbopts
860             ) or croak("connection failed\n");
861             my %have = map( {$_ => 1} $dbh->tables);
862             $have{drawing} or croak("$spec has no drawing table");
863             $have{layer} or croak("$spec has no layer table");
864             my ($had) = $dbh->selectrow_array(
865             "SELECT dwg_name from drawing where dwg_name = ?",
866             {},
867             $drawing
868             );
869             defined($had) or croak("$spec / $drawing does not exists\n");
870             my $col = $dbh->selectcol_arrayref(
871             "select layer_name from layer where dwg_name=?",
872             {}, $drawing) or
873             croak "get layers failed";
874             my @layers = @$col;
875             my %tntr = (
876             "arcs" => "arcs",
877             "circles" => "circles",
878             "lines" => "lines",
879             "plines" => "polyline", # FIXME: rename that table!
880             "points" => "points",
881             "texts" => "texts",
882             "images" => "images",
883             );
884             my %se_h;
885             $se_h{layers} = $dbh->prepare(
886             "SELECT layer_id " .
887             "FROM layer " .
888             "WHERE layer_name = ? " .
889             "AND dwg_name = ? "
890             );
891              
892              
893             my %del_h;
894             foreach my $type (keys(%tntr)) {
895             $have{$tntr{$type}} || next;
896             $del_h{$type} = $dbh->prepare(
897             "DELETE from " . $tntr{$type} . " " .
898             "WHERE layer_id = ?"
899             );
900             }
901             $del_h{layer} = $dbh->prepare(
902             "DELETE from layer where layer_id = ?"
903             );
904              
905             foreach my $layer (@layers) {
906             $se_h{layers}->execute($layer, $drawing)
907             or croak("cannot lookup $layer in $drawing\n");
908             my ($layer_id) = $se_h{layers}->fetchrow_array();
909             foreach my $type (keys(%del_h)) {
910             $del_h{$type}->execute($layer_id);
911             $del_h{$type}->finish();
912             }
913             $del_h{layer}->execute($layer_id);
914             }
915             $se_h{layers}->finish();
916              
917             $dbh->do(
918             "DELETE from drawing WHERE dwg_name = ?",
919             {
920             AutoCommit => 1,
921             },
922             $drawing
923             );
924             unless($dbopts{AutoCommit}) {
925             $dbh->commit or
926             croak("commit failed:\n", $dbh->errstr);
927             }
928             $dbh->disconnect();
929            
930              
931              
932             } # end subroutine cleardb definition
933             ########################################################################
934              
935             =head1 Internals
936              
937             =cut
938             ########################################################################
939              
940             =head2 parse_options
941              
942             Allows options to come in through the $spec or %opts.
943              
944             %options = parse_options($spec, \%opts);
945              
946             =cut
947             sub parse_options {
948             my ($spec, $options) = @_;
949             my %opts;
950             (ref($options) eq "HASH" ) && (%opts = %$options);
951             $opts{auth} && (
952             ($opts{username}, $opts{password}) = @{$opts{auth}}
953             );
954             unless($opts{drawing}) {
955             if($spec =~ s/drawing=(.*?)//) {
956             $opts{drawing} = $1;
957             $spec =~ s/;+/;/;
958             $spec =~ s/;$//;
959             }
960             else {
961             croak("no drawing found in spec or opts\n");
962             }
963             }
964             $opts{spec} = $spec;
965             return(%opts);
966             } # end subroutine parse_options definition
967             ########################################################################
968              
969             =head2 sort_addr
970              
971             Sorts through @addr_list and returns a hash of array references for each
972             entity type.
973              
974             %these = sort_addr($layer, \@addr_list);
975              
976             =cut
977             sub sort_addr {
978             my ($layer, $list) = @_;
979             # print "list: @$list\n";
980             my @valid = grep({$_->{layer} eq $layer} @$list);
981             my @ents = sort(keys(%call_syntax));
982             # init the refs
983             my %these = map({$_ => []} @ents);
984             foreach my $addr (@valid) {
985             push(@{$these{$addr->{type}}}, $addr);
986             }
987             return(%these);
988             } # end subroutine sort_addr definition
989             ########################################################################
990              
991             1;