File Coverage

blib/lib/XML/RDB.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             #####
2             #
3             # $Id: RDB.pm,v 1.1 2003/04/18 00:33:17 trostler Exp $
4             #
5             # COPYRIGHT AND LICENSE
6             # Copyright (c) 2001, 2003, Juniper Networks, Inc. All rights reserved.
7             # Redistribution and use in source and binary forms, with or without
8             # modification, are permitted provided that the following conditions are
9             # met:
10             # 1. Redistributions of source code must retain the above
11             # copyright notice, this list of conditions and the following
12             # disclaimer.
13             # 2. Redistributions in binary form must reproduce the above
14             # copyright notice, this list of conditions and the following disclaimer
15             # in the documentation and/or other materials provided with the
16             # distribution.
17             # 3. The name of the copyright owner may not be used to
18             # endorse or promote products derived from this software without specific
19             # prior written permission.
20             # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21             # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22             # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23             # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24             # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25             # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26             # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27             # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28             # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29             # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30             # POSSIBILITY OF SUCH DAMAGE.
31             #
32             #####
33              
34             package XML::RDB;
35              
36 1     1   52549 use XML::DOM;
  0            
  0            
37             use XML::RDB::MakeTables;
38             use XML::RDB::PopulateTables;
39             use XML::RDB::UnpopulateTables;
40             use XML::RDB::UnpopulateSchema;
41             use strict;
42              
43             #####
44             #
45             # Copyright (c) 2001-2003 Juniper Networks, Inc.
46             # All rights reserved.
47             #
48             # Set of common routines & constants
49             #
50             #####
51             BEGIN {
52             use Exporter ();
53             use vars qw($VERSION); # @ISA @EXPORT);
54             $VERSION = "1.3";
55             # @ISA = qw(Exporter);
56             # @EXPORT = qw(
57             # );
58             }
59              
60             sub new {
61             my ($class, %arg) = @_;
62             my $config;
63              
64             if ($arg{config_file}) {
65             open(C, "$arg{config_file}") || die "$!";
66             while() {
67             next if (/^(#|$)/);
68             next unless (my ($key, $value) = $_ =~ /^([A-Z_]+)=(.+)/);
69             chomp $value;
70             $key =~ s/\s//g;
71             $value =~ s/\s//g;
72             $config->{$key} = $value if ($key);
73             }
74             close(C);
75             }
76             elsif ( scalar(keys(%arg)) > 0 ) {
77             $config = \%arg;
78             }
79             else {
80             warn "$class requires a DSN for DBI->connect\n";
81             return undef;
82             }
83              
84             # setup connection
85             my $dbh = DBI->connect(
86             $config->{DSN},
87             ($config->{DB_USERNAME} || ''),
88             ($config->{DB_PASSWORD} || ''),
89             { RaiseError => $config->{DB_RAISEERROR} || 0,
90             PrintError => $config->{DB_PRINTERROR} || 0,
91             # NOTE :
92             # autocommit needs to be on for this app in its current state.
93             # 1. the method of inserts, and updates
94             # 2. DBIx::Sequence
95             AutoCommit => 1 }
96             ) or (( warn $!) and ( return undef));
97            
98             if ( $config->{DSN} =~ /dbi:sqlite/i) {
99             warn("SQLite detected...setting SQLite PRAGMA: synchronus = OFF; locking_mode = EXCLUSIVE\n")
100             if ($config->{DB_PRINTERR});
101             $dbh->do('PRAGMA synchronous = OFF') || warn $dbh->errstr;
102             $dbh->do('PRAGMA locking_mode = EXCLUSIVE') || warn $dbh->errstr;
103             }
104            
105             $DBIx::Recordset::Debug = $config->{DBIX_DEBUG} || 0;
106             $DBIx::Recordset::FetchsizeWarn = $config->{DBIX_FETCHSIZEWARN} || 0;
107              
108             my $self = bless {
109             DSN => $config->{DSN},
110             DB_CATALOG => (defined $config->{DB_CATALOG})
111             ? $config->{DB_CATALOG}
112             : undef,
113             DBH => $dbh,
114             TEXT_COLUMN => ($config->{TEXT_WIDTH})
115             ? 'varchar('. $config->{TEXT_WIDTH} .')'
116             : 'varchar(50)',
117             TEXT_WIDTH => $config->{TEXT_WIDTH} || 50,
118             TAB => ($config->{TAB}) ? ' ' x $config->{TAB} : ' ',
119             PK_NAME => $config->{PK_NAME} || 'id',
120             FK_NAME => $config->{FK_NAME} || 'fk',
121             TABLE_PREFIX => $config->{TABLE_PREFIX} || 'gen',
122             DB_USERNAME => $config->{DB_USERNAME} || '',
123             DB_PASSWORD => $config->{DB_PASSWORD} || '',
124             _HEADERS => (defined $config->{SQL_HEADERS})
125             ? $config->{SQL_HEADERS} : 1,
126             _SELECTS => (defined $config->{SQL_SELECTS})
127             ? $config->{SQL_SELECTS} : 1,
128             _SQLITE => ($config->{DSN} =~ /dbi:sqlite/i) ? 1 : 0,
129             }, $class;
130              
131             $self->{REAL_ELEMENT_NAME_TABLE} = $self->mtn('element_names');
132             $self->{LINK_TABLE_NAMES_TABLE} = $self->mtn('link_tables');
133             $self->{ROOT_TABLE_N_PK_TABLE} = $self->mtn('root_n_pk');
134             return $self;
135             }
136              
137             sub done { my $self = shift; $self->DESTROY; return $self};
138              
139             sub DESTROY {
140             my $self = shift;
141              
142             # NOTE : Recordset specific
143             # Undef takes the name of a typglob and will destroy the array, the
144             # hash, and the object. All unwritten data is written to the db.
145             # All db connections are closed and all memory is freed.
146             # DBIx::Recordset::Undef ($name)
147             # DBIx::Recordset->Flush();
148             $self->{DBH}->disconnect() if ($self->{DBH});
149             $self->{_DOC}->dispose if ($self->{_DOC});
150             }
151              
152             sub drop_tables {
153             my $self = shift;
154             return warn "Please add DB_CATALOG for the dsn supplied for dropping tables.\n"
155             unless ( defined $self->{DB_CATALOG} );
156             my $dbh = $self->{DBH};
157             my ($driver, $dbname) = $self->{DSN} =~ /dbi:(\w+):\w+=(\w+)/i;
158             my $sth;
159              
160             if ( $self->{DB_CATALOG} == 0 ) {
161             $sth = $dbh->table_info(undef, $dbname, undef, undef);
162             }
163             elsif ( $self->{DB_CATALOG} == 1 ) {
164             $sth = $dbh->table_info($dbname, 'public', undef, undef);
165             }
166             else {
167             return warn 'Please fix DB_CATALOG to boolean (1|0) in your dsn config file.'
168             .'examples : Postgres 1, SQLite 0, Mysql 0' ."\n";
169             }
170            
171             my $d_tables;
172             my $regex = '^'. $self->{TABLE_PREFIX} .'_\w+';
173             for my $rel (@{$sth->fetchall_arrayref({})}) {
174             if ($rel->{TABLE_NAME} =~ /$regex/o) {
175             push(@{ $d_tables }, $rel->{TABLE_NAME});
176             $dbh->do('DROP TABLE '. $rel->{TABLE_NAME})
177             }
178             }
179             return $d_tables;
180             }
181              
182             sub create_tables {
183             my ($self, $file) = @_;
184             my $statement;
185            
186             open (F, $file) || die $!;
187             while () {
188             next if (/^(-|$)/); chomp;
189            
190             if (s/;$//) {
191             $self->{DBH}->do($statement . $_) || die $self->{DBH}->errstr;
192             $statement = '';
193             }
194             else {
195             $statement .= $_;
196             }
197             }
198             close(F);
199             return $self;
200             }
201              
202             sub _get_xml {
203             my $self = shift;
204             my $xmlfile = shift;
205              
206             if (($self->{_XMLFILE}) and
207             ((!$xmlfile) or ($self->{_XMLFILE} eq $xmlfile))) {
208             undef;
209             }
210             elsif ($self->{DOC}) {
211             $self->{_DOC}->dispose;
212             $self->{_XMLFILE} = $xmlfile;
213             $self->{_DOC} = $self->{_XMLPARSER}->parsefile($xmlfile);
214             $self->{_HEAD} = $self->{_DOC}->getDocumentElement;
215             }
216             else {
217             $self->{_XMLFILE} = $xmlfile;
218             $self->{_XMLPARSER} = new XML::DOM::Parser;
219             $self->{_DOC} = $self->{_XMLPARSER}->parsefile($xmlfile) || die "$!";
220             $self->{_HEAD} = $self->{_DOC}->getDocumentElement;
221             }
222             return ($self->{_DOC},$self->{_HEAD});
223             }
224              
225             sub make_tables {
226             my ($self, $xmlfile, $outfile) = @_;
227             my ($doc, $head) = $self->_get_xml($xmlfile);
228             my $mt = new XML::RDB::MakeTables($self, $doc, $head, $outfile);
229             $mt->go;
230             }
231              
232             sub populate_tables {
233             my ($self, $xmlfile) = @_;
234             my ($doc, $head) = $self->_get_xml($xmlfile);
235             my $pt = new XML::RDB::PopulateTables($self, $doc, $head);
236             $pt->go;
237             }
238              
239             sub unpopulate_tables {
240             my $self = shift;
241             my $outfile = shift;
242             my $root_n_pk = $self->get_root_n_pk_db();
243             my $ut = new XML::RDB::UnpopulateTables($self,$outfile);
244             $ut->go($root_n_pk->{root}, $root_n_pk->{pk});
245             }
246              
247             sub unpopulate_schema {
248             my $us = new XML::RDB::UnpopulateSchema(@_);
249             $us->go;
250             }
251              
252             #
253             # Blow thru all nodes & find 1:N relationships between them
254             # Need to pass the root of yer XML::DOM tree
255             # Quite pleasant really
256             # Basically just count the number of duplicate elements under
257             # this one - if > 1 then 1:N relationship
258             #
259             sub find_one_to_n_relationships {
260             # NOTE : Replaced the recursive loop with goto's with a @stack
261             my($self, $head) = @_;
262             my (@stack,%saw,$one_to_n);
263             TOP_REL:
264             my $nodes = [ $head->getChildNodes ];
265             %saw = ();
266              
267             # Count duplicates
268             grep($saw{$_->getNodeName}++, @{$nodes});
269             foreach (keys %saw) {
270             next if /#text/;
271             next if /#comment/;
272             next if ($one_to_n->{$head->getNodeName}{$_});
273             if ($saw{$_} > 1) {
274             $one_to_n->{$head->getNodeName}{$_} = 1;
275             }
276             }
277              
278             TOPLESS_REL:
279             # And recurse on down the road
280             while (scalar(@{$nodes})) {
281             my $sub_node = shift(@{$nodes});
282             next if ($sub_node->getNodeType == XML::DOM::TEXT_NODE);
283             push(@stack, $nodes);
284             $head = $sub_node;
285             goto TOP_REL;
286             }
287              
288             if (scalar(@stack) > 0) {
289             $nodes = pop(@stack);
290             goto TOPLESS_REL;
291             }
292              
293             # Done is Done
294             return $one_to_n;
295             }
296              
297             #
298             # Helper function to dump the 'one_to_n' datastructure out
299             #
300             sub dump_otn {
301             my $self = shift;
302             my($otn, $char) = @_;
303             my $ret;
304              
305             foreach my $one (keys %$otn) {
306             foreach my $many (keys %{$otn->{$one}}) {
307             $ret .= "${char}\t$one -> $many\n";
308             }
309             }
310             return $ret;
311             }
312              
313             #
314             # 'mtn' = 'Make Table Name' - cleans up text so it's a valid
315             # DB table name & adds TABLE_PREFIX
316             #
317             sub mtn {
318             my($self, $base, $out) = @_;
319             ($out = $self->{TABLE_PREFIX} . "_$base") =~ y/#:\.-/_____/;
320             lc $out;
321             }
322            
323             # Normalize column/table names - No ':' or '-' & all lower case
324             sub normalize {
325             my($in,$out) = shift;
326            
327             # Get rid of funny stuff
328             # ($out = $in) =~ s/[#:\.-]/_/go;
329             ($out = $in) =~ y/#:\.-/_____/;
330             lc $out;
331             }
332              
333             #
334             # Pulls out root_n_pk data from the DB itself
335             #
336             sub get_root_n_pk_db {
337             my $self = shift;
338             my $rt_n_pk;
339              
340             if ($self->{_SQLITE}) {
341             my $sth = $self->{DBH}->prepare('select * from '. $self->{ROOT_TABLE_N_PK_TABLE});
342             $sth->execute();
343             my $row = $sth->fetch();
344             $rt_n_pk = { root => $row->[1], pk => $row->[0] }
345             if ($row);
346             }
347             else {
348             use vars qw(*root_n_pk);
349             *root_n_pk = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
350             '!Table' => $self->{ROOT_TABLE_N_PK_TABLE}});
351             my $root;
352             $root = $root_n_pk[0] if (@root_n_pk);
353             $rt_n_pk = { root => $root->{root}, pk => $root->{pk} } if ($root);
354             DBIx::Recordset::Undef ('*root_n_pk');
355             }
356             return $rt_n_pk;
357             }
358              
359             #
360             # Pulls out one_to_n data structures from the DB itself
361             #
362             sub get_one_to_n_db {
363             my $self = shift;
364             my %one_to_n;
365              
366             if ($self->{_SQLITE}) {
367             my $sth = $self->{DBH}->prepare('select * from '. $self->{LINK_TABLE_NAMES_TABLE});
368             $sth->execute();
369             while (my $row = $sth->fetch()) {
370             $one_to_n{$row->[0]}{$row->[1]} = 1;
371             }
372             }
373             else {
374             use vars qw(*link_tables);
375             *link_tables = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
376             '!Table' => $self->{LINK_TABLE_NAMES_TABLE}});
377             foreach my $links (@link_tables) {
378             $one_to_n{$links->{one_table}}{$links->{many_table}} = 1;
379             }
380             # *link_tables->Flush();
381             DBIx::Recordset::Undef ('*link_tables');
382             }
383             return \%one_to_n;
384             }
385              
386             sub get_real_element_names_db {
387             my $self = shift;
388             my $names;
389              
390             if ($self->{_SQLITE}) {
391             my $sth = $self->{DBH}->prepare('select * from '. $self->{REAL_ELEMENT_NAME_TABLE});
392             $sth->execute();
393             while (my $row = $sth->fetch()) {
394             $names->{$row->[0]} = $row->[1];
395             }
396             }
397             else {
398             use vars qw(*set);
399             *set = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
400             '!Table' => $self->{REAL_ELEMENT_NAME_TABLE}});
401             foreach my $n (@set) {
402             $names->{$n->{db_name}} = $n->{xml_name};
403             }
404             # *link_tables->Flush();
405             DBIx::Recordset::Undef ('*set');
406             }
407             return $names;
408             }
409              
410              
411             #
412             # Get the corresponding 'xml_name' from this 'db_name'
413             #
414             #sub get_xml_name {
415             # my($self, $db_name) = @_;
416             # my @set = DBIx::Recordset->Search({
417             # '!DataSource' => $self->{DBH},
418             # '!Table' => $self->{REAL_ELEMENT_NAME_TABLE},
419             # '$where' => 'db_name = ?',
420             # '$values' => [ $db_name ],
421             # });
422             #
423             # $set[0]{xml_name};
424             #}
425              
426             #
427             # Recursive routine to unpopulate DB into in-memory data structure
428             #
429             # Takes a table name, PK of a row, & a hash ref of where in this
430             # giant monstrous data in-memory data-structure we've created to
431             # stick this row's values
432             #
433              
434             sub un_populate_table {
435             my($self, $one_to_n, $table_name, $id, $put_it_here) = @_;
436             my $match = "_" . $self->{PK_NAME};
437             my(@stack, $schema_names, $jj, $other_table, $fk_field, $links, $kk, $i );
438             use vars qw(*schema); # DBIx::Recordset likes GLOBs, yummy
439              
440             TOP_TBL:
441             ( $jj, $other_table, $fk_field, $links, $kk, $i ) = ();
442            
443             # This'll get the ball rolling...
444             *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
445             '!Table' => $table_name,
446             $self->{PK_NAME} => $id
447             });
448             # Any column that ends in '_id' we gotta assume is a 1:1 map
449             # Any column that ends in '_value' is text in this element
450             # Any column that ends in '_attribute' is an attribute
451              
452             $schema_names = $schema->Names;
453              
454             # foreach my $col (@{$schema->Names}) {
455             for ($jj = 0; scalar(@{$schema_names}) > $jj; $jj++) {
456             my $col = $schema_names->[$jj];
457             my $val = $schema[0]{$col};
458              
459             if ($col =~ /${match}$/o) {
460             # 1:1
461             next if (!$val);
462              
463             # Unpopulate sub table since this is a foreign key
464             my $this_table;
465             ($this_table = $col) =~ s/${match}$//;
466             # $self->un_populate_table($one_to_n, $other_table, $val, \%{$put_it_here->{$other_table}});
467              
468             push(@stack, [ $table_name, $id, $put_it_here, $schema_names, $jj,
469             $other_table, $fk_field, $links, $kk, $i ]);
470             ($table_name, $id, $put_it_here ) =
471             ($this_table, $val, \%{$put_it_here->{$this_table}});
472             goto TOP_TBL;
473             TOPLESS_TBL_1to1:
474              
475             # Put the candle - back
476             *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
477             '!Table' => $table_name,
478             $self->{PK_NAME} => $id});
479              
480             }
481             elsif ($col =~ /_value$/o) {
482             # text between tag
483             $put_it_here->{value} = $val if (defined $val);
484             }
485             elsif ($col =~ /_attribute$/o) {
486             # attribute
487             $put_it_here->{attribute}{$col} = $val if (defined $val);
488             }
489             elsif ($col eq $self->{PK_NAME}) {
490             # PK - don't do anything
491             }
492             elsif ($col =~ /_$self->{FK_NAME}$/o ) {
493             # FK - don't do anything
494             }
495             else {
496             # Keep us honest
497             die "I don't know what to do with column name $col = $val!\n";
498             }
499             }
500              
501             # Now get 1:N relationships
502             if ($one_to_n->{$table_name}) {
503             # Go thru each 'N' relationship table to this one
504              
505             $links = [ keys %{$one_to_n->{$table_name}} ];
506              
507             for ($kk = 0; scalar(@{$links}) > $kk; $kk++) {
508             my $link = $links->[$kk];
509              
510             # Look up other PK via our PK (which is an FK in the sub_table)
511             $other_table = $self->mtn($link);
512             $fk_field = $table_name."_" . $self->{FK_NAME};
513              
514             # Look up matching row in other (linked) table
515             # Get rows from other table with a $fk_field matching
516             # this one's
517             *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
518             '!Table' => $other_table,
519             "$fk_field" => $id});
520              
521             $i = 0;
522             while(my $other_pk = $schema[$i]{$self->{PK_NAME}}) {
523             next if (!$other_pk);
524             # use $i in name to keep 'em seperate
525             # & recursively unpopulate that other table
526             # $self->un_populate_table($one_to_n, $other_table, $other_pk, \%{$put_it_here->{$i}{"${other_table}"}});
527              
528             push(@stack, [ $table_name, $id, $put_it_here, $schema_names, $jj,
529             $other_table, $fk_field, $links, $kk, $i ]);
530             ($table_name, $id, $put_it_here ) =
531             ($other_table, $other_pk, \%{$put_it_here->{$i}{"${other_table}"}});
532            
533             goto TOP_TBL;
534             TOPLESS_TBL_FK:
535              
536             # Put The Candle - Back
537             *schema = DBIx::Recordset->Search({'!DataSource'=>$self->{DBH},
538             '!Table' => $other_table,
539             "$fk_field" => $id});
540             $i++;
541             }
542             }
543             }
544              
545             if (scalar(@stack) > 0) {
546             ( $table_name, $id, $put_it_here, $schema_names, $jj,
547             $other_table, $fk_field, $links, $kk, $i ) = @{pop(@stack)};
548             unless ($other_table) { goto TOPLESS_TBL_1to1; }
549             else { goto TOPLESS_TBL_FK; }
550             }
551              
552             DBIx::Recordset::Undef ('*schema');
553             }
554              
555              
556              
557              
558             #sub un_populate_table {
559             # my($self, $one_to_n, $table_name, $id, $put_it_here) = @_;
560             # use vars qw(*schema); # DBIx::Recordset likes GLOBs, yummy
561             #
562             # # This'll get the ball rolling...
563             # *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
564             # '!Table' => $table_name,
565             # $self->{PK_NAME} => $id
566             # });
567             # # Any column that ends in '_id' we gotta assume is a 1:1 map
568             # # Any column that ends in '_value' is text in this element
569             # # Any column that ends in '_attribute' is an attribute
570             # foreach my $col (@{$schema->Names}) {
571             # my $val = $schema[0]{$col};
572             # my $match = "_" . $self->{PK_NAME};
573             #
574             # if ($col =~ /${match}$/) {
575             # # 1:1
576             # next if (!$val);
577             #
578             # # Unpopulate sub table since this is a foreign key
579             # my $other_table;
580             # ($other_table = $col) =~ s/${match}$//;
581             # $self->un_populate_table($one_to_n, $other_table, $val, \%{$put_it_here->{$other_table}});
582             # # Put the candle - back
583             # *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
584             # '!Table' => $table_name,
585             # $self->{PK_NAME} => $id});
586             #
587             # }
588             # elsif ($col =~ /_value$/) {
589             # # text between tag
590             # $put_it_here->{value} = $val if (defined $val);
591             # }
592             # elsif ($col =~ /_attribute$/) {
593             # # attribute
594             # $put_it_here->{attribute}{$col} = $val if (defined $val);
595             # }
596             # elsif ($col eq $self->{PK_NAME}) {
597             # # PK - don't do anything
598             # }
599             # elsif ($col =~ /_$self->{FK_NAME}$/ ) {
600             # # FK - don't do anything
601             # }
602             # else {
603             # # Keep us honest
604             # die "I don't know what to do with column name $col = $val!\n";
605             # }
606             # }
607             #
608             # # Now get 1:N relationships
609             # if ($one_to_n->{$table_name}) {
610             # # Go thru each 'N' relationship table to this one
611             # foreach my $link (keys %{$one_to_n->{$table_name}}) {
612             # # Look up other PK via our PK (which is an FK in the sub_table)
613             # my $other_table = $self->mtn($link);
614             # my $fk_field = $table_name."_" . $self->{FK_NAME};
615             #
616             # # Look up matching row in other (linked) table
617             # # Get rows from other table with a $fk_field matching
618             # # this one's
619             # *schema = DBIx::Recordset->Search({'!DataSource' => $self->{DBH},
620             # '!Table' => $other_table,
621             # "$fk_field" => $id});
622             # my $i = 0;
623             # while(my $other_pk = $schema[$i]{$self->{PK_NAME}}) {
624             # next if (!$other_pk);
625             # # use $i in name to keep 'em seperate
626             # # & recursively unpopulate that other table
627             # $self->un_populate_table($one_to_n, $other_table, $other_pk, \%{$put_it_here->{$i}{"${other_table}"}});
628             #
629             # # Put The Candle - Back
630             # *schema = DBIx::Recordset->Search({'!DataSource'=>$self->{DBH},
631             # '!Table' => $other_table,
632             # "$fk_field" => $id});
633             # $i++;
634             # }
635             # }
636             # }
637             ## *schema->Flush();
638             # DBIx::Recordset::Undef ('*schema');
639             #}
640              
641             1;
642             __END__