/[cvs]/nfo/perl/libs/Data/Transfer/Sync/Core.pm
ViewVC logotype

Annotation of /nfo/perl/libs/Data/Transfer/Sync/Core.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Sun Jan 19 01:23:04 2003 UTC (21 years, 5 months ago) by joko
Branch: MAIN
+ new from Data/Transfer/Sync.pm

1 joko 1.1 ## $Id: Sync.pm,v 1.11 2002/12/23 07:10:59 joko Exp $
2     ##
3     ## Copyright (c) 2002 Andreas Motl <andreas.motl@ilo.de>
4     ##
5     ## See COPYRIGHT section in pod text below for usage and distribution rights.
6     ##
7     ## ----------------------------------------------------------------------------------------
8     ## $Log: Sync.pm,v $
9     ## Revision 1.11 2002/12/23 07:10:59 joko
10     ## + using MD5 for checksum generation again - the 32-bit integer hash from DBI seems to be too lazy
11     ##
12     ## Revision 1.10 2002/12/19 01:07:16 joko
13     ## + fixed output done via $logger
14     ##
15     ## Revision 1.9 2002/12/16 07:02:34 jonen
16     ## + added comment
17     ##
18     ## Revision 1.8 2002/12/15 02:03:09 joko
19     ## + fixed logging-messages
20     ## + additional metadata-checks
21     ##
22     ## Revision 1.7 2002/12/13 21:49:34 joko
23     ## + sub configure
24     ## + sub checkOptions
25     ##
26     ## Revision 1.6 2002/12/06 04:49:10 jonen
27     ## + disabled output-puffer here
28     ##
29     ## Revision 1.5 2002/12/05 08:06:05 joko
30     ## + bugfix with determining empty fields (Null) with DBD::CSV
31     ## + debugging
32     ## + updated comments
33     ##
34     ## Revision 1.4 2002/12/03 15:54:07 joko
35     ## + {import}-flag is now {prepare}-flag
36     ##
37     ## Revision 1.3 2002/12/01 22:26:59 joko
38     ## + minor cosmetics for logging
39     ##
40     ## Revision 1.2 2002/12/01 04:43:25 joko
41     ## + mapping deatil entries may now be either an ARRAY or a HASH
42     ## + erase flag is used now (for export-operations)
43     ## + expressions to refer to values inside deep nested structures
44     ## - removed old mappingV2-code
45     ## + cosmetics
46     ## + sub _erase_all
47     ##
48     ## Revision 1.1 2002/11/29 04:45:50 joko
49     ## + initial check in
50     ##
51     ## Revision 1.1 2002/10/10 03:44:21 cvsjoko
52     ## + new
53     ## ----------------------------------------------------------------------------------------
54    
55    
56     package Data::Transfer::Sync::Core;
57    
58     use strict;
59     use warnings;
60    
61     use mixin::with qw( Data::Transfer::Sync );
62    
63    
64     # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - main
65    
66     use Data::Dumper;
67    
68     use misc::HashExt;
69     use libp qw( md5_base64 );
70     use libdb qw( quotesql hash2Sql );
71     use Data::Transform::Deep qw( hash2object refexpr2perlref );
72     use Data::Compare::Struct qw( getDifference isEmpty );
73     use Data::Storage::Container;
74     use DesignPattern::Object;
75    
76     # get logger instance
77     my $logger = Log::Dispatch::Config->instance;
78    
79     $| = 1;
80    
81     =pod
82     sub new {
83     my $invocant = shift;
84     my $class = ref($invocant) || $invocant;
85     my $self = {};
86     $logger->debug( __PACKAGE__ . "->new(@_)" );
87     bless $self, $class;
88     $self->configure(@_);
89     return $self;
90     }
91     =cut
92    
93    
94     sub _init {
95     my $self = shift;
96    
97     # build new container if necessary
98     $self->{container} = Data::Storage::Container->new() if !$self->{container};
99    
100     # add storages to container (optional)
101     foreach (keys %{$self->{storages}}) {
102     $self->{container}->addStorage($_, $self->{storages}->{$_});
103     }
104    
105     return 1;
106    
107     }
108    
109     sub _initV1 {
110     my $self = shift;
111     # tag storages with id-authority and checksum-provider information
112     # TODO: better store tag inside metadata to hold bits together!
113     map { $self->{container}->{storage}->{$_}->{isIdentAuthority} = 1 } @{$self->{id_authorities}};
114     map { $self->{container}->{storage}->{$_}->{isChecksumAuthority} = 1; } @{$self->{checksum_authorities}};
115     map { $self->{container}->{storage}->{$_}->{isWriteProtected} = 1; } @{$self->{write_protected}};
116     }
117    
118    
119     sub _preCheckOptions {
120    
121     my $self = shift;
122     my $opts = shift;
123    
124     #print Dumper($opts);
125     #exit;
126    
127     # the type of the to-be-synced item
128     if (!$opts->{source}->{nodeType}) {
129     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-type\".");
130     return;
131     }
132     # the name of the (container-) node the items are listed in
133     if (!$opts->{source}->{nodeName}) {
134     $logger->error( __PACKAGE__ . "->_preCheckOptions failed: Please specify \"source-node\".");
135     return;
136     }
137    
138     # a "map"-declaration which module to use for mapping- and/or lookup-purposes
139     if (!$opts->{map}) {
140     $logger->warning( __PACKAGE__ . "->_preCheckOptions: No mapping supplied - please check key 'map|mappings' in global configuration or specify additional argument '--mapping-module'.");
141     return;
142     }
143     if (!$opts->{map}->{moduleName}) {
144     $logger->warning( __PACKAGE__ . "->_preCheckOptions: Currently only perl-modules can provide mappings: Please specify one with '--mapping-module'.");
145     return;
146     }
147    
148     return 1;
149    
150     }
151    
152    
153    
154    
155    
156    
157    
158    
159    
160     sub _buildFieldmappingV1 {
161     my $self = shift;
162    
163     # build mapping
164     # incoming: and Array of node map entries (Array or Hash) - e.g.
165     # [ 'source:item_name' => 'target:class_val' ]
166     # { source => 'event->startDateTime', target => 'begindate' }
167     foreach (@{$self->{args}->{mapping}}) {
168     if (ref $_ eq 'ARRAY') {
169     my @entry1 = split(':', $_->[0]);
170     my @entry2 = split(':', $_->[1]);
171     my $descent = [];
172     my $node = [];
173     $descent->[0] = $entry1[0];
174     $descent->[1] = $entry2[0];
175     $node->[0] = $entry1[1];
176     $node->[1] = $entry2[1];
177     push @{$self->{meta}->{$descent->[0]}->{childnodes}}, $node->[0];
178     push @{$self->{meta}->{$descent->[1]}->{childnodes}}, $node->[1];
179     } elsif (ref $_ eq 'HASH') {
180     foreach my $entry_key (keys %$_) {
181     my $entry_val = $_->{$entry_key};
182     push @{$self->{meta}->{$entry_key}->{childnodes}}, $entry_val;
183     }
184     }
185    
186     }
187    
188     }
189    
190     sub _buildMetadataV1 {
191     my $self = shift;
192    
193     # decompose identifiers for each partner
194     # TODO: refactor!!! take this list from already established/given metadata
195     foreach ('source', 'target') {
196    
197     # get/set metadata for further processing
198    
199     # Partner and Node (e.g.: "L:Country" or "R:countries.csv")
200     if (my $item = $self->{args}->{$_}) {
201     my @item = split(':', $item);
202     $self->{meta}->{$_}->{dbkey} = $item[0];
203     $self->{meta}->{$_}->{node} = $item[1];
204     }
205    
206     # Filter
207     if (my $item_filter = $self->{args}->{$_ . '_filter'}) {
208     $self->{meta}->{$_}->{filter} = $item_filter;
209     }
210    
211     # IdentProvider
212     if (my $item_ident = $self->{args}->{$_ . '_ident'}) {
213     my @item_ident = split(':', $item_ident);
214     $self->{meta}->{$_}->{IdentProvider} = { method => $item_ident[0], arg => $item_ident[1] };
215     }
216    
217     #print Dumper($self->{meta});
218    
219     # TODO: ChecksumProvider
220    
221     # exclude properties/subnodes
222     if (my $item_exclude = $self->{args}->{$_ . '_exclude'}) {
223     $self->{meta}->{$_}->{subnodes_exclude} = $item_exclude;
224     }
225    
226     # TypeProvider
227     if (my $item_type = $self->{args}->{$_ . '_type'}) {
228     my @item_type = split(':', $item_type);
229     $self->{meta}->{$_}->{TypeProvider} = { method => $item_type[0], arg => $item_type[1] };
230     }
231    
232     # Callbacks - writers (will be triggered _before_ writing to target)
233     if (my $item_writers = $self->{args}->{$_ . '_callbacks_write'}) {
234     my $descent = $_; # this is important since the following code inside the map wants to use its own context variables
235     map { $self->{meta}->{$descent}->{Callback}->{write}->{$_}++; } @$item_writers;
236     }
237    
238     # Callbacks - readers (will be triggered _after_ reading from source)
239     if (my $item_readers = $self->{args}->{$_ . '_callbacks_read'}) {
240     my $descent = $_;
241     map { $self->{meta}->{$descent}->{Callback}->{read}->{$_}++; } @$item_readers;
242     }
243    
244     # resolve storage objects
245     #$self->{$_} = $self->{container}->{storage}->{$self->{meta}->{$_}->{dbkey}};
246     # relink references to metainfo
247     $self->{meta}->{$_}->{storage} = $self->{container}->{storage}->{$self->{meta}->{$_}->{dbkey}};
248     #print "iiiiisprov: ", Dumper($self->{meta}->{$_}->{storage}), "\n";
249     }
250    
251     }
252    
253     # TODO: abstract the hardwired use of "source" and "target" in here somehow - hmmmm....... /(="§/%???
254     sub _syncNodes {
255    
256     my $self = shift;
257    
258     my $tc = OneLineDumpHash->new( {} );
259     my $results;
260    
261     # set of objects is already in $self->{args}
262     # TODO: make independent of the terminology "object..."
263     $results = $self->{args}->{objectSet} if $self->{args}->{objectSet};
264    
265     # apply filter
266     if (my $filter = $self->{meta}->{source}->{filter}) {
267     #print Dumper($filter);
268     #exit;
269     $results ||= $self->_getNodeList('source', $filter);
270     }
271    
272     # get reference to node list from convenient method provided by CORE-HANDLE
273     #$results ||= $self->{source}->getListUnfiltered($self->{meta}->{source}->{node});
274     #$results ||= $self->{meta}->{source}->{storage}->getListUnfiltered($self->{meta}->{source}->{node});
275     $results ||= $self->_getNodeList('source');
276    
277     # checkpoint: do we actually have a list to iterate through?
278     if (!$results || !@{$results}) {
279     $logger->notice( __PACKAGE__ . "->syncNodes: No nodes to synchronize." );
280     return;
281     }
282    
283     # dereference
284     my @results = @{$results};
285    
286     # iterate through set
287     foreach my $source_node_real (@results) {
288    
289     $tc->{total}++;
290    
291     #print "======================== iter", "\n";
292    
293     # clone object (in case we have to modify it here)
294     # TODO:
295     # - is a "deep_copy" needed here if occouring modifications take place?
296     # - puuhhhh, i guess a deep_copy would destroy tangram mechanisms?
297     # - after all, just take care for now that this object doesn't get updated!
298     my $source_node = $source_node_real;
299    
300     # modify entry - handle new style callbacks (the readers)
301     #print Dumper($source_node);
302     #exit;
303    
304     my $descent = 'source';
305    
306     # handle callbacks right now while scanning them (asymmetric to the writers)
307     my $map_callbacks = {};
308     if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {
309    
310     my $error = 0;
311    
312     foreach my $node (keys %{$callbacks->{read}}) {
313    
314     my $object = $source_node;
315     my $value; # = $source_node->{$node};
316    
317     # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
318     my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_read';
319     my $evalstring = 'return ' . $perl_callback . '( { object => $object, property => $node, value => $value, storage => $self->{meta}->{$descent}->{storage} } );';
320     #print $evalstring, "\n"; exit;
321     my $cb_result = eval($evalstring);
322     if ($@) {
323     die $@;
324     $error = 1;
325     print $@, "\n";
326     }
327     # ------------ half-redundant: make $self->callCallback($object, $value, $opts)
328    
329     $source_node->{$node} = $cb_result;
330    
331     }
332    
333     }
334    
335     #print Dumper($source_node);
336    
337     # exclude defined fields (simply delete from object)
338     map { delete $source_node->{$_} } @{$self->{meta}->{source}->{subnodes_exclude}};
339    
340     # here we accumulate information about the status of the current node (payload/row/object/item/entry)
341     $self->{node} = {};
342     $self->{node}->{source}->{payload} = $source_node;
343    
344     #print "res - ident", "\n";
345    
346     # determine ident of entry
347     my $identOK = $self->_resolveNodeIdent('source');
348     #if (!$identOK && lc $self->{args}->{direction} ne 'import') {
349     if (!$identOK) {
350     #print Dumper($self->{meta}->{source});
351     $logger->critical( __PACKAGE__ . "->syncNodes: No ident found in source node \"$self->{meta}->{source}->{node}\", try to \"prepare\" this node first?" );
352     return;
353     }
354    
355     #print "statload", "\n";
356     #print "ident: ", $self->{node}->{source}->{ident}, "\n";
357     #print Dumper($self->{node});
358    
359     my $statOK = $self->_statloadNode('target', $self->{node}->{source}->{ident});
360    
361     #print Dumper($self->{node});
362    
363     # mark node as new either if there's no ident or if stat/load failed
364     if (!$statOK) {
365     $self->{node}->{status}->{new} = 1;
366     print "n" if $self->{verbose};
367     }
368    
369     #print "checksum", "\n";
370    
371     # determine status of entry by synchronization method
372     if ( (lc $self->{args}->{method} eq 'checksum') ) {
373     #if ( $statOK && (lc $self->{args}->{method} eq 'checksum') ) {
374     #if ( !$self->{node}->{status}->{new} && (lc $self->{args}->{method} eq 'checksum') ) {
375    
376     # TODO:
377     # is this really worth a "critical"???
378     # no - it should just be a debug appendix i believe
379    
380     #print "readcs", "\n";
381    
382     # calculate checksum of source node
383     #$self->_calcChecksum('source');
384     if (!$self->_readChecksum('source')) {
385     $logger->critical( __PACKAGE__ . "->_readChecksum: Could not find \"source\" entry with ident=\"$self->{node}->{source}->{ident}\"" );
386     $tc->{skip}++;
387     print "s" if $self->{verbose};
388     next;
389     }
390    
391     # get checksum from synchronization target
392     $self->_readChecksum('target');
393     #if (!$self->_readChecksum('target')) {
394     # $logger->critical( __PACKAGE__ . "->_readChecksum: Could not find \"target\" entry with ident=\"$self->{node}->{source}->{ident}\"" );
395     # next;
396     #}
397    
398     # pre flight check: do we actually have a checksum provided?
399     #if (!$self->{node}->{source}->{checksum}) {
400     # print "Source checksum for entry with ident \"$self->{node}->{source}->{ident}\" could not be calculated, maybe it's missing?.", "\n";
401     # return;
402     #}
403    
404     # determine if entry is "new" or "dirty"
405     # after all, this seems to be the point where the hammer falls.....
406     print "c" if $self->{verbose};
407     $self->{node}->{status}->{new} = !$self->{node}->{target}->{checksum};
408     if (!$self->{node}->{status}->{new}) {
409     $self->{node}->{status}->{dirty} =
410     $self->{node}->{status}->{new} ||
411     (!$self->{node}->{source}->{checksum} || !$self->{node}->{target}->{checksum}) ||
412     ($self->{node}->{source}->{checksum} ne $self->{node}->{target}->{checksum}) ||
413     $self->{args}->{force};
414     }
415    
416     }
417    
418     # first reaction on entry-status: continue with next entry if the current is already "in sync"
419     if (!$self->{node}->{status}->{new} && !$self->{node}->{status}->{dirty}) {
420     $tc->{in_sync}++;
421     next;
422     }
423    
424     # build map to actually transfer the data from source to target
425     $self->_buildMap();
426    
427    
428     #print Dumper($self->{node}); exit;
429    
430     #print "attempt", "\n";
431    
432     # additional (new) checks for feature "write-protection"
433     if ($self->{meta}->{target}->{storage}->{isWriteProtected}) {
434     $tc->{attempt_transfer}++;
435     print "\n" if $self->{verbose};
436     $logger->notice( __PACKAGE__ . "->syncNodes: Target is write-protected. Will not insert or modify node. " .
437     "(Ident: $self->{node}->{source}->{ident} " . "Dump:\n" . Dumper($self->{node}->{source}->{payload}) . ")" );
438     print "\n" if $self->{verbose};
439     $tc->{skip}++;
440     next;
441     }
442    
443     # transfer contents of map to target
444     if ($self->{node}->{status}->{new}) {
445     $tc->{attempt_new}++;
446     $self->_doTransferToTarget('insert');
447     # asymmetry: refetch node from target to re-calculate new ident and checksum (TODO: is IdentAuthority of relevance here?)
448     #print Dumper($self->{node});
449     $self->_statloadNode('target', $self->{node}->{target}->{ident}, 1);
450     $self->_readChecksum('target');
451    
452     } elsif ($self->{node}->{status}->{dirty}) {
453     $tc->{attempt_modify}++;
454     # asymmetry: get ident before updating (TODO: is IdentAuthority of relevance here?)
455     $self->{node}->{target}->{ident} = $self->{node}->{map}->{$self->{meta}->{target}->{IdentProvider}->{arg}};
456     $self->_doTransferToTarget('update');
457     $self->_readChecksum('target');
458     }
459    
460     if ($self->{node}->{status}->{ok}) {
461     $tc->{ok}++;
462     print "t" if $self->{verbose};
463     }
464    
465     if ($self->{node}->{status}->{error}) {
466     $tc->{error}++;
467     push( @{$tc->{error_per_row}}, $self->{node}->{status}->{error} );
468     print "e" if $self->{verbose};
469     }
470    
471     # change ident in source (take from target), if transfer was ok and target is an IdentAuthority
472     # this is (for now) called a "retransmit" indicated by a "r"-character when verbosing
473     if ($self->{node}->{status}->{ok} && $self->{meta}->{target}->{storage}->{isIdentAuthority}) {
474     print "r" if $self->{verbose};
475     #print Dumper($self->{meta});
476     #print Dumper($self->{node});
477     #exit;
478     $self->_doModifySource_IdentChecksum($self->{node}->{target}->{ident});
479     }
480    
481     print ":" if $self->{verbose};
482    
483     }
484    
485     print "\n" if $self->{verbose};
486    
487     # build user-message from some stats
488     my $msg = "statistics: $tc";
489    
490     if ($tc->{error_per_row}) {
491     $msg .= "\n";
492     $msg .= "errors from \"error_per_row\":" . "\n";
493     $msg .= Dumper($tc->{error_per_row});
494     }
495    
496     # todo!!!
497     #sysevent( { usermsg => $msg, level => $level }, $taskEvent );
498     $logger->info( __PACKAGE__ . "->syncNodes: $msg" );
499    
500     return $tc;
501    
502     }
503    
504    
505     # refactor this as some core-function to do a generic dump resolving data-encapsulations of e.g. Set::Object
506     sub _dumpCompact {
507     my $self = shift;
508    
509     #my $vars = \@_;
510     my @data = ();
511    
512     my $count = 0;
513     foreach (@_) {
514     my $item = {};
515     foreach my $key (keys %$_) {
516     my $val = $_->{$key};
517    
518     #print Dumper($val);
519    
520     if (ref $val eq 'Set::Object') {
521     #print "========================= SET", "\n";
522     #print Dumper($val);
523     #print Dumper($val->members());
524     #$val = $val->members();
525     #$vars->[$count]->{$key} = $val->members() if $val->can("members");
526     #$item->{$key} = $val->members() if $val->can("members");
527     $item->{$key} = $val->members();
528     #print Dumper($vars->[$count]->{$key});
529    
530     } else {
531     $item->{$key} = $val;
532     }
533    
534     }
535     push @data, $item;
536     $count++;
537     }
538    
539     #print "Dump:", Dumper(@data), "\n";
540    
541     $Data::Dumper::Indent = 0;
542     my $result = Dumper(@data);
543     $Data::Dumper::Indent = 2;
544     return $result;
545    
546     }
547    
548    
549     sub _calcChecksum {
550    
551     my $self = shift;
552     my $descent = shift;
553     my $specifier = shift;
554    
555     # calculate checksum for current object
556     my $ident = $self->{node}->{$descent}->{ident};
557    
558     # build dump of this node
559     my $payload = $self->{node}->{$descent}->{payload};
560     #my $dump = $ident . "\n" . $item->quickdump();
561     #my $dump = $ident . "\n" . Dumper($item);
562     my $dump = $ident . "\n" . $self->_dumpCompact($payload);
563    
564     # TODO: $logger->dump( ... );
565     #$logger->debug( __PACKAGE__ . ": " . $dump );
566     #$logger->dump( __PACKAGE__ . ": " . $dump );
567    
568     # calculate checksum from dump
569     # note: the 32-bit integer hash from DBI seems
570     # to generate duplicates with small payloads already in ranges of hundreds of items/rows!!!
571     # try to avoid to use it or try to use it only for payloads greater than, hmmm, let's say 30 chars?
572     # (we had about 15 chars average per item (row))
573    
574     # md5-based fingerprint, base64 encoded (from Digest::MD5)
575     $self->{node}->{$descent}->{checksum} = md5_base64($dump) . '==';
576     # 32-bit integer "hash" value (maybe faster?) (from DBI)
577     #$self->{node}->{$descent}->{checksum} = DBI::hash($dump, 1);
578    
579     # signal good
580     return 1;
581    
582     }
583    
584    
585     sub _readChecksum {
586     my $self = shift;
587    
588     my $descent = shift;
589    
590     #print "getcheck:", "\n"; print Dumper($self->{node}->{$descent});
591    
592     if (!$self->{node}->{$descent}) {
593     # signal checksum bad
594     return;
595     }
596    
597     # get checksum for current entry
598     # TODO: don't have the checksum column/property hardcoded as "cs" here, make this configurable somehow
599    
600     if ($self->{meta}->{$descent}->{storage}->{isChecksumAuthority}) {
601     #$self->{node}->{$descent}->{checksum} = $entry->{cs};
602     #$self->{node}->{$descent}->{checksum} = $self->_calcChecksum($descent); # $entry->{cs};
603     #print "descent: $descent", "\n";
604     $self->_calcChecksum($descent);
605     #print "checksum: ", $self->{node}->{$descent}->{checksum}, "\n";
606     } else {
607    
608     #$self->{node}->{$descent}->{checksum} = $entry->{cs};
609     $self->{node}->{$descent}->{checksum} = $self->{node}->{$descent}->{payload}->{cs};
610     }
611    
612     # signal checksum good
613     return 1;
614    
615     }
616    
617    
618     sub _buildMap {
619    
620     my $self = shift;
621    
622     # field-structure for building sql
623     # mapping of sql-fieldnames to object-attributes
624     $self->{node}->{map} = {};
625    
626     # manually set ...
627     # ... object-id
628     $self->{node}->{map}->{$self->{meta}->{target}->{IdentProvider}->{arg}} = $self->{node}->{source}->{ident};
629     # ... checksum
630     $self->{node}->{map}->{cs} = $self->{node}->{source}->{checksum};
631    
632     #print "sqlmap: ", Dumper($self->{node}->{map}), "\n";
633    
634     # for transferring flat structures via simple (1:1) mapping
635     # TODO: diff per property / property value
636    
637     if ($self->{args}->{mapping}) {
638     # apply mapping from $self->{args}->{mapping} to $self->{node}->{map}
639     #foreach my $key (@{$self->{meta}->{source}->{childnodes}}) {
640     my @childnodes = @{$self->{meta}->{source}->{childnodes}};
641     for (my $mapidx = 0; $mapidx <= $#childnodes; $mapidx++) {
642     #my $map_right = $self->{args}->{mapping}->{$key};
643    
644     $self->{node}->{source}->{propcache} = {};
645     $self->{node}->{target}->{propcache} = {};
646    
647     # get property name
648     $self->{node}->{source}->{propcache}->{property} = $self->{meta}->{source}->{childnodes}->[$mapidx];
649     $self->{node}->{target}->{propcache}->{property} = $self->{meta}->{target}->{childnodes}->[$mapidx];
650     #print "map: $map_right", "\n";
651    
652     # get property value
653     my $value;
654    
655     # detect for callback - old style - (maybe the better???)
656     if (ref($self->{node}->{target}->{map}) eq 'CODE') {
657     #$value = &$map_right($objClone);
658     } else {
659     # plain (scalar?) value
660     #$value = $objClone->{$map_right};
661     $self->{node}->{source}->{propcache}->{value} = $self->{node}->{source}->{payload}->{$self->{node}->{source}->{propcache}->{property}};
662     }
663     #$self->{node}->{map}->{$key} = $value;
664    
665     # detect expression
666     # for transferring deeply nested structures described by expressions
667     #print "val: $self->{node}->{source}->{propcache}->{value}", "\n";
668     if ($self->{node}->{source}->{propcache}->{property} =~ s/^expr://) {
669    
670     # create an anonymous sub to act as callback target dispatcher
671     my $cb_dispatcher = sub {
672     #print "=============== CALLBACK DISPATCHER", "\n";
673     #print "ident: ", $self->{node}->{source}->{ident}, "\n";
674     #return $self->{node}->{source}->{ident};
675    
676     };
677    
678    
679     #print Dumper($self->{node});
680    
681     # build callback map for helper function
682     #my $cbmap = { $self->{meta}->{source}->{IdentProvider}->{arg} => $cb_dispatcher };
683     my $cbmap = {};
684     my $value = refexpr2perlref($self->{node}->{source}->{payload}, $self->{node}->{source}->{propcache}->{property}, $cbmap);
685     $self->{node}->{source}->{propcache}->{value} = $value;
686     }
687    
688     # encode values dependent on type of underlying storage here - expand cases...
689     my $storage_type = $self->{meta}->{target}->{storage}->{locator}->{type};
690     if ($storage_type eq 'DBI') {
691     # ...for sql
692     $self->{node}->{source}->{propcache}->{value} = quotesql($self->{node}->{source}->{propcache}->{value});
693     }
694     elsif ($storage_type eq 'Tangram') {
695     # iso? utf8 already possible?
696    
697     } elsif ($storage_type eq 'LDAP') {
698     # TODO: encode utf8 here?
699     }
700    
701     # store value to transfer map
702     $self->{node}->{map}->{$self->{node}->{target}->{propcache}->{property}} = $self->{node}->{source}->{propcache}->{value};
703    
704     }
705     }
706    
707    
708     # TODO: $logger->dump( ... );
709     #$logger->debug( "sqlmap:" . "\n" . Dumper($self->{node}->{map}) );
710     #print "sqlmap: ", Dumper($self->{node}->{map}), "\n";
711     #print "entrystatus: ", Dumper($self->{node}), "\n";
712    
713     }
714    
715     sub _resolveNodeIdent {
716     my $self = shift;
717     my $descent = shift;
718    
719     #print Dumper($self->{node}->{$descent});
720    
721     # get to the payload
722     #my $item = $specifier->{item};
723     my $payload = $self->{node}->{$descent}->{payload};
724    
725     # resolve method to get to the id of the given item
726     # we use global metadata and the given descent for this task
727     #my $ident = $self->{$descent}->id($item);
728     #my $ident = $self->{meta}->{$descent}->{storage}->id($item);
729    
730     my $ident;
731     my $provider_method = $self->{meta}->{$descent}->{IdentProvider}->{method};
732     my $provider_arg = $self->{meta}->{$descent}->{IdentProvider}->{arg};
733    
734     # resolve to ident
735     if ($provider_method eq 'property') {
736     $ident = $payload->{$provider_arg};
737    
738     } elsif ($provider_method eq 'storage_method') {
739     #$ident = $self->{meta}->{$descent}->{storage}->id($item);
740     $ident = $self->{meta}->{$descent}->{storage}->$provider_arg($payload);
741     }
742    
743     $self->{node}->{$descent}->{ident} = $ident;
744    
745     return 1 if $ident;
746    
747     }
748    
749    
750     sub _modifyNode {
751     my $self = shift;
752     my $descent = shift;
753     my $action = shift;
754     my $map = shift;
755     my $crit = shift;
756    
757     # map for new style callbacks
758     my $map_callbacks = {};
759    
760     # checks go first!
761    
762     # TODO: this should be reviewed first - before extending ;-)
763     # TODO: this should be extended:
764     # count this cases inside the caller to this sub and provide a better overall message
765     # if this counts still zero in the end:
766     # "No nodes have been touched for modify: Do you have column-headers in your csv file?"
767     if (not defined $self->{node}) {
768     #$logger->critical( __PACKAGE__ . "->_modifyNode failed: \"$descent\" node is empty." );
769     #return;
770     }
771    
772     # transfer callback nodes from value map to callback map - handle them afterwards! - (new style callbacks)
773     if (my $callbacks = $self->{meta}->{$descent}->{Callback}) {
774     foreach my $callback (keys %{$callbacks->{write}}) {
775     $map_callbacks->{write}->{$callback} = $map->{$callback};
776     delete $map->{$callback};
777     }
778     }
779    
780    
781     #print Dumper($self->{meta});
782    
783     # DBI speaks SQL
784     if ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'DBI') {
785    
786     #print Dumper($self->{node});
787     my $sql_main;
788     # translate map to sql
789     #print $action, "\n"; exit;
790     #print $self->{meta}->{$descent}->{node}, "\n"; exit;
791     #print "action:";
792     #print $action, "\n";
793     #$action = "anc";
794     #print "yai", "\n";
795    
796     #print Dumper($map);
797     #delete $map->{cs};
798    
799     if (lc($action) eq 'insert') {
800     $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_INSERT');
801     } elsif (lc $action eq 'update') {
802     $crit ||= "$self->{meta}->{$descent}->{IdentProvider}->{arg}='$self->{node}->{$descent}->{ident}'";
803     $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_UPDATE', $crit);
804     }
805    
806     #$sql_main = "UPDATE currencies_csv SET oid='abcdef' WHERE text='Australian Dollar' AND key='AUD';";
807     #$sql_main = "UPDATE currencies_csv SET oid='huhu2' WHERE ekey='AUD'";
808    
809     #print "sql: ", $sql_main, "\n";
810     #exit;
811    
812     # transfer data
813     my $sqlHandle = $self->{meta}->{$descent}->{storage}->sendCommand($sql_main);
814    
815     #exit;
816    
817     # handle errors
818     if ($sqlHandle->err) {
819     #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
820     $self->{node}->{status}->{error} = {
821     statement => $sql_main,
822     state => $sqlHandle->state,
823     err => $sqlHandle->err,
824     errstr => $sqlHandle->errstr,
825     };
826     } else {
827     $self->{node}->{status}->{ok} = 1;
828     }
829    
830     # Tangram does it the oo-way (naturally)
831     } elsif ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'Tangram') {
832     my $sql_main;
833     my $object;
834    
835     # determine classname
836     my $classname = $self->{meta}->{$descent}->{node};
837    
838     # properties to exclude
839     my @exclude = @{$self->{meta}->{$descent}->{subnodes_exclude}};
840    
841    
842     if (my $identProvider = $self->{meta}->{$descent}->{IdentProvider}) {
843     push @exclude, $identProvider->{arg};
844     }
845    
846     # new feature:
847     # - check TypeProvider metadata property from other side
848     # - use argument (arg) inside as a classname for object creation on this side
849     #my $otherSide = $self->_otherSide($descent);
850     if (my $typeProvider = $self->{meta}->{$descent}->{TypeProvider}) {
851     #print Dumper($map);
852     $classname = $map->{$typeProvider->{arg}};
853     # remove nodes from map also (push nodes to "subnodes_exclude" list)
854     push @exclude, $typeProvider->{arg};
855     }
856    
857     # exclude banned properties (remove from map)
858     #map { delete $self->{node}->{map}->{$_} } @{$self->{args}->{exclude}};
859     map { delete $map->{$_} } @exclude;
860    
861     # list of properties
862     my @props = keys %{$map};
863    
864     # transfer data
865     if (lc $action eq 'insert') {
866    
867     # build array to initialize object
868     #my @initarray = ();
869     #map { push @initarray, $_, undef; } @props;
870    
871     # make the object persistent in four steps:
872     # - raw create (perl / class tangram scope)
873     # - engine insert (tangram scope) ... this establishes inheritance - don't try to fill in inherited properties before!
874     # - raw fill-in from hash (perl scope)
875     # - engine update (tangram scope) ... this updates all properties just filled in
876    
877     # create new object ...
878     #my $object = $classname->new( @initarray );
879     $object = $classname->new();
880    
881     # ... pass to orm ...
882     $self->{meta}->{$descent}->{storage}->insert($object);
883    
884     # ... and initialize with empty (undef'd) properties.
885     #print Dumper(@props);
886     map { $object->{$_} = undef; } @props;
887    
888     # mix in values ...
889     hash2object($object, $map);
890    
891     # ... and re-update@orm.
892     #print Dumper($object);
893     $self->{meta}->{$descent}->{storage}->update($object);
894    
895     # asymmetry: get ident after insert
896     # TODO:
897     # - just do this if it is an IdentAuthority
898     # - use IdentProvider metadata here
899     #print Dumper($self->{meta}->{$descent});
900     my $oid = $self->{meta}->{$descent}->{storage}->id($object);
901     #print "oid: $oid", "\n";
902     $self->{node}->{$descent}->{ident} = $oid;
903    
904    
905     } elsif (lc $action eq 'update') {
906    
907     # get fresh object from orm first
908     $object = $self->{meta}->{$descent}->{storage}->load($self->{node}->{$descent}->{ident});
909    
910     #print Dumper($self->{node});
911    
912     # mix in values
913     #print Dumper($object);
914     hash2object($object, $map);
915     #print Dumper($object);
916     #exit;
917     $self->{meta}->{$descent}->{storage}->update($object);
918     }
919    
920     my $error = 0;
921    
922     # handle new style callbacks - this is a HACK - do this without an eval!
923     #print Dumper($map);
924     #print "cb: ", Dumper($self->{meta}->{$descent}->{Callback});
925     #print Dumper($map_callbacks);
926     foreach my $node (keys %{$map_callbacks->{write}}) {
927     #print Dumper($node);
928     my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_write';
929     my $evalstring = $perl_callback . '( { object => $object, value => $map_callbacks->{write}->{$node}, storage => $self->{meta}->{$descent}->{storage} } );';
930     #print $evalstring, "\n"; exit;
931     eval($evalstring);
932     if ($@) {
933     $error = 1;
934     print $@, "\n";
935     }
936    
937     #print "after eval", "\n";
938    
939     if (!$error) {
940     # re-update@orm
941     $self->{meta}->{$descent}->{storage}->update($object);
942     }
943     }
944    
945     # handle errors
946     if ($error) {
947     #print "error", "\n";
948     =pod
949     my $sqlHandle;
950     #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; }
951     $self->{node}->{status}->{error} = {
952     statement => $sql_main,
953     state => $sqlHandle->state,
954     err => $sqlHandle->err,
955     errstr => $sqlHandle->errstr,
956     };
957     =cut
958     # rollback....
959     #print "rollback", "\n";
960     $self->{meta}->{$descent}->{storage}->erase($object);
961     #print "after rollback", "\n";
962     } else {
963     $self->{node}->{status}->{ok} = 1;
964     }
965    
966    
967     }
968    
969     }
970    
971     # TODO:
972     # this should be split up into...
973     # - a "_statNode" (should just touch the node to check for existance)
974     # - a "_loadNode" (should load node completely)
975     # - maybe additionally a "loadNodeProperty" (may specify properties to load)
976     # - introduce $self->{nodecache} for this purpose
977     # TODO:
978     # should we:
979     # - not pass ident in here but resolve it via "$descent"?
980     # - refactor this and stuff it with additional debug/error message
981     # - this = the way the implicit load mechanism works
982     sub _statloadNode {
983    
984     my $self = shift;
985     my $descent = shift;
986     my $ident = shift;
987     my $force = shift;
988    
989     # fetch entry to retrieve checksum from
990     # was:
991     if (!$self->{node}->{$descent} || $force) {
992     # is:
993     #if (!$self->{node}->{$descent}->{item} || $force) {
994    
995     if (!$ident) {
996     #print "\n", "Attempt to fetch entry implicitely by ident failed: no ident given! This may result in an insert if no write-protection is in the way.", "\n";
997     return;
998     }
999    
1000     # patch for DBD::CSV
1001     if ($ident && $ident eq 'Null') {
1002     return;
1003     }
1004    
1005     #print "yai!", "\n";
1006    
1007     my $query = {
1008     node => $self->{meta}->{$descent}->{node},
1009     subnodes => [qw( cs )],
1010     criterias => [
1011     { key => $self->{meta}->{$descent}->{IdentProvider}->{arg},
1012     op => 'eq',
1013     val => $ident },
1014     ]
1015     };
1016    
1017     #print Dumper($query);
1018    
1019     my $result = $self->{meta}->{$descent}->{storage}->sendQuery($query);
1020    
1021     my $entry = $result->getNextEntry();
1022    
1023     #print Dumper($entry);
1024     #print "pers: " . $self->{meta}->{$descent}->{storage}->is_persistent($entry), "\n";
1025     #my $state = $self->{meta}->{$descent}->{storage}->_fetch_object_state($entry, { name => 'TransactionHop' } );
1026     #print Dumper($state);
1027    
1028     my $status = $result->getStatus();
1029    
1030     #print Dumper($status);
1031    
1032     # TODO: enhance error handling (store inside tc)
1033     #if (!$row) {
1034     # print "\n", "row error", "\n";
1035     # next;
1036     #}
1037    
1038     # these checks run before actually loading payload- and meta-data to node-container
1039    
1040     # 1st level - hard error
1041     if ($status && $status->{err}) {
1042     $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - hard error (that's ok): $status->{err}" );
1043     return;
1044     }
1045    
1046     # 2nd level - logical (empty/notfound) error
1047     if (($status && $status->{empty}) || !$entry) {
1048     $logger->debug( __PACKAGE__ . "->_statloadNode (ident=\"$ident\") failed - logical error (that's ok)" );
1049     #print "no entry (logical)", "\n";
1050     return;
1051     }
1052    
1053     #print Dumper($entry);
1054    
1055     # was:
1056     # $self->{node}->{$descent}->{ident} = $ident;
1057     # is:
1058     # TODO: re-resolve ident from entry via metadata "IdentProvider" here - like elsewhere
1059     $self->{node}->{$descent}->{ident} = $ident;
1060     $self->{node}->{$descent}->{payload} = $entry;
1061    
1062     }
1063    
1064     return 1;
1065    
1066     }
1067    
1068     sub _doTransferToTarget {
1069     my $self = shift;
1070     my $action = shift;
1071     $self->_modifyNode('target', $action, $self->{node}->{map});
1072     }
1073    
1074     sub _doModifySource_IdentChecksum {
1075     my $self = shift;
1076     my $ident_new = shift;
1077     # this changes an old node to a new one including ident and checksum
1078     # TODO:
1079     # - eventually introduce an external resource to store this data to
1080     # - we won't have to "re"-modify the source node here
1081     my $map = {
1082     $self->{meta}->{source}->{IdentProvider}->{arg} => $ident_new,
1083     cs => $self->{node}->{target}->{checksum},
1084     };
1085    
1086     #print Dumper($map);
1087     #print Dumper($self->{node});
1088     #exit;
1089    
1090     $self->_modifyNode('source', 'update', $map);
1091     }
1092    
1093    
1094     # this is a shortcut method
1095     # ... let's try to avoid _any_ redundant code in here (ok... - at the cost of method lookups...)
1096     sub _getNodeList {
1097     my $self = shift;
1098     my $descent = shift;
1099     my $filter = shift;
1100     return $self->{meta}->{$descent}->{storage}->getListFiltered($self->{meta}->{$descent}->{node}, $filter);
1101     }
1102    
1103    
1104     sub _prepareNode_MetaProperties {
1105     my $self = shift;
1106     my $descent = shift;
1107    
1108     $logger->info( __PACKAGE__ . "->_prepareNode_MetaProperties( descent $descent )" );
1109    
1110     # TODO: this should (better) be: "my $firstnode = $self->_getFirstNode($descent);"
1111     my $list = $self->_getNodeList($descent);
1112    
1113     # get first node
1114     my $firstnode = $list->[0];
1115    
1116     # check if node contains meta properties/nodes
1117     # TODO: "cs" is hardcoded here!
1118     my @required = ( $self->{meta}->{$descent}->{IdentProvider}->{arg}, 'cs' );
1119     my @found = keys %$firstnode;
1120     #my @diff = getDifference(\@found, \@required);
1121     my $diff = getDifference(\@required, \@found);
1122     #print Dumper(@found);
1123     #print Dumper(@required);
1124     #print Dumper(@diff);
1125     #if (!$#diff || $#diff == -1) {
1126     if (isEmpty($diff)) {
1127     $logger->warning( __PACKAGE__ . "->_prepareNode_MetaProperties: node is lacking meta properties - will try to alter..." );
1128     foreach (@required) {
1129     my $sql = "ALTER TABLE $self->{meta}->{$descent}->{node} ADD COLUMN $_";
1130     #print "sql: $sql", "\n";
1131     my $res = $self->{meta}->{$descent}->{storage}->sendCommand($sql);
1132     #print Dumper($res->getStatus());
1133     }
1134     }
1135    
1136     }
1137    
1138     sub _prepareNode_DummyIdent {
1139     my $self = shift;
1140     my $descent = shift;
1141    
1142     $logger->info( __PACKAGE__ . "->_prepareNode_DummyIdent( descent $descent )" );
1143    
1144     my $list = $self->_getNodeList($descent);
1145     #print Dumper($list);
1146     my $i = 0;
1147     my $ident_base = 5678983;
1148     my $ident_appendix = '0001';
1149     foreach my $node (@$list) {
1150     my $ident_dummy = $i + $ident_base;
1151     $ident_dummy .= $ident_appendix;
1152     my $map = {
1153     $self->{meta}->{$descent}->{IdentProvider}->{arg} => $ident_dummy,
1154     cs => undef,
1155     };
1156    
1157     # diff lists and ...
1158     my $diff = getDifference([keys %$node], [keys %$map]);
1159     next if $#{$diff} == -1;
1160    
1161     # ... build criteria including all columns
1162     my @crits;
1163     foreach my $property (@$diff) {
1164     next if !$property;
1165     my $value = $node->{$property};
1166     next if !$value;
1167     push @crits, "$property='" . quotesql($value) . "'";
1168     }
1169     my $crit = join ' AND ', @crits;
1170     print "p" if $self->{verbose};
1171    
1172     #print Dumper($map);
1173     #print Dumper($crit);
1174    
1175     $self->_modifyNode($descent, 'update', $map, $crit);
1176     $i++;
1177     }
1178    
1179     print "\n" if $self->{verbose};
1180    
1181     if (!$i) {
1182     $logger->warning( __PACKAGE__ . "->_prepareNode_DummyIdent: no nodes touched" );
1183     }
1184    
1185     }
1186    
1187     # TODO: handle this in an abstract way (wipe out use of 'source' and/or 'target' inside core)
1188     sub _otherSide {
1189     my $self = shift;
1190     my $descent = shift;
1191     return 'source' if $descent eq 'target';
1192     return 'target' if $descent eq 'source';
1193     return '';
1194     }
1195    
1196     sub _erase_all {
1197     my $self = shift;
1198     my $descent = shift;
1199     #my $node = shift;
1200     my $node = $self->{meta}->{$descent}->{node};
1201     $self->{meta}->{$descent}->{storage}->eraseAll($node);
1202     }
1203    
1204    
1205     =pod
1206    
1207    
1208     =head1 DESCRIPTION
1209    
1210     Data::Transfer::Sync is a module providing a generic synchronization process
1211     across arbitrary/multiple storages based on a ident/checksum mechanism.
1212     It sits on top of Data::Storage.
1213    
1214    
1215     =head1 REQUIREMENTS
1216    
1217     For full functionality:
1218     Data::Storage
1219     Data::Transform
1220     Data::Compare
1221     ... and all their dependencies
1222    
1223    
1224     =head1 AUTHORS / COPYRIGHT
1225    
1226     The Data::Storage module is Copyright (c) 2002 Andreas Motl.
1227     All rights reserved.
1228    
1229     You may distribute it under the terms of either the GNU General Public
1230     License or the Artistic License, as specified in the Perl README file.
1231    
1232    
1233     =head1 SUPPORT / WARRANTY
1234    
1235     Data::Storage is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
1236    
1237    
1238    
1239     =head1 BUGS
1240    
1241     When in "import" mode for windows file - DBD::AutoCSV may hang.
1242     Hint: Maybe the source node contains an ident-, but no checksum-column?
1243    
1244    
1245     =head1 USER LEVEL ERRORS
1246    
1247     =head4 Mapping
1248    
1249     - - - - - - - - - - - - - - - - - - - - - - - - - -
1250     info: BizWorks::Process::Setup->syncResource( source_node Currency mode PULL erase 0 import 0 )critical: BizWorks::Process::Setup->startSync: Can't access mapping for node "Currency" - please check BizWorks::ResourceMapping.
1251     - - - - - - - - - - - - - - - - - - - - - - - - - -
1252     You have to create a sub for each node used in synchronization inside named Perl module. The name of this sub _must_ match
1253     the name of the node you want to sync. This sub holds mapping metadata to give the engine hints about how
1254     to access the otherwise generic nodes.
1255     - - - - - - - - - - - - - - - - - - - - - - - - - -
1256    
1257    
1258     =head4 DBD::AutoCSV's rulebase
1259    
1260     - - - - - - - - - - - - - - - - - - - - - - - - - -
1261     info: BizWorks::Process::Setup->syncResource( source_node Currency mode PULL erase 0 import 0 )
1262     info: Data::Transfer::Sync->syncNodes: source=L/Currency <- target=R/currencies.csv
1263    
1264     Execution ERROR: Error while scanning: Missing first row or scanrule not applied. at C:/home/amo/develop/netfrag.org/nfo/perl/libs/DBD/CSV.p
1265     m line 165, <GEN9> line 1.
1266     called from C:/home/amo/develop/netfrag.org/nfo/perl/libs/Data/Storage/Handler/DBI.pm at 123.
1267    
1268     DBI-Error: DBD::AutoCSV::st fetchrow_hashref failed: Attempt to fetch row from a Non-SELECT statement
1269     notice: Data::Transfer::Sync->syncNodes: No nodes to synchronize.
1270     - - - - - - - - - - - - - - - - - - - - - - - - - -
1271     DBD::AutoCSV contains a rulebase which is spooled down while attempting to guess the style of the csv file regarding
1272     parameters like newline (eol), column-seperation-character (sep_char), quoting character (quote_char).
1273     If this spool runs out of entries and no style could be resolved, DBD::CSV dies causing this "Execution ERROR" which
1274     results in a "DBI-Error" afterwards.
1275     - - - - - - - - - - - - - - - - - - - - - - - - - -
1276    
1277    
1278     =head4 Check structure of source node
1279    
1280     - - - - - - - - - - - - - - - - - - - - - - - - - -
1281     info: Data::Transfer::Sync->syncNodes: source=L/Currency <- target=R/currencies.csv
1282     critical: Data::Transfer::Sync->syncNodes: Can not synchronize: No ident found in source node, maybe try to "import" this node first.
1283     - - - - - - - - - - - - - - - - - - - - - - - - - -
1284     If lowlevel detection succeeds, but no other required informations are found, this message is issued.
1285     "Other informations" might be:
1286     - column-header-row completely missing
1287     - ident column is empty
1288     - - - - - - - - - - - - - - - - - - - - - - - - - -
1289    
1290    
1291     =head4 Modify structure of source node
1292    
1293     - - - - - - - - - - - - - - - - - - - - - - - - - -
1294     info: Data::Transfer::Sync->syncNodes: source=L/Currency <- target=R/currencies.csv
1295     info: Data::Transfer::Sync->_prepareNode_MetaProperties( descent source )
1296     warning: Data::Transfer::Sync->_prepareNode_MetaProperties: node is lacking meta properties - will try to alter...
1297     SQL ERROR: Command 'ALTER' not recognized or not supported!
1298    
1299     SQL ERROR: Command 'ALTER' not recognized or not supported!
1300     - - - - - - - - - - - - - - - - - - - - - - - - - -
1301     The Engine found a node which structure does not match the required. It tries to alter this automatically - only when doing "import" -
1302     but the DBD driver (in this case DBD::CSV) gets in the way croaking not to be able to do this.
1303     This could also appear if your database connection has insufficient rights to modify the database structure.
1304     DBD::CSV croaks because it doesn't implement the ALTER command, so please edit your columns manually.
1305     Hint: Add columns with the names of your "ident" and "checksum" property specifications.
1306     - - - - - - - - - - - - - - - - - - - - - - - - - -
1307    
1308    
1309     =head4 Load source node by ident
1310    
1311     - - - - - - - - - - - - - - - - - - - - - - - - - -
1312     info: Data::Transfer::Sync->_prepareNode_DummyIdent( descent source )
1313     pcritical: Data::Transfer::Sync->_modifyNode failed: "source" node is empty.
1314     - - - - - - - - - - - - - - - - - - - - - - - - - -
1315     The source node could not be loaded. Maybe the ident is missing. Please check manually.
1316     Hint: Like above, the ident and/or checksum columns may be missing....
1317     - - - - - - - - - - - - - - - - - - - - - - - - - -
1318    
1319    
1320     =head1 TODO
1321    
1322     - sub _resolveIdentProvider
1323     - wrap _doModifySource and _doTransferTarget around a core function which can change virtually any type of node
1324     - split this module up into Sync.pm, Sync/Core.pm, Sync/Compare.pm and Sync/Compare/Checksum.pm
1325     - introduce _compareNodes as a core method and wrap it around methods in Sync/Compare/Checksum.pm
1326     - introduce Sync/Compare/MyComparisonImplementation.pm
1327     - some generic deferring method - e.g. "$self->defer(action)" - to be able to accumulate a bunch of actions for later processing
1328     - this implies everything done is _really_ split up into generic actions - how else would we defer them???
1329     - example uses:
1330     - fetch whole checksum list from node
1331     - remember source ident retransmits
1332     - remember: this is convenient - and maybe / of course faster - but we'll loose "per-node-atomic" operations
1333     - feature: mechanism to implicit inject checksum property to nodes (alter table / modify schema)
1334     - expand statistics / keep track of:
1335     - touched/untouched nodes
1336     - full sync
1337     - just do a push and a pull for now but use stats for touched nodes in between to speed up things
1338     - introduce some new metadata flags for a synchronization partner which is (e.g.) of "source" or "target":
1339     - isNewNodePropagator
1340     - isWriteProtected
1341    
1342    
1343     =cut
1344    
1345     1;

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed