--- nfo/perl/libs/Data/Transfer/Sync/Core.pm 2003/01/20 17:01:14 1.3 +++ nfo/perl/libs/Data/Transfer/Sync/Core.pm 2004/06/19 01:45:08 1.12 @@ -1,11 +1,44 @@ -## $Id: Core.pm,v 1.3 2003/01/20 17:01:14 joko Exp $ +## ------------------------------------------------------------------------- +## +## $Id: Core.pm,v 1.12 2004/06/19 01:45:08 joko Exp $ ## ## Copyright (c) 2002 Andreas Motl ## ## See COPYRIGHT section in pod text below for usage and distribution rights. ## -## ---------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------- ## $Log: Core.pm,v $ +## Revision 1.12 2004/06/19 01:45:08 joko +## introduced "local checksum"-mechanism +## moved _dumpCompact to ::Compare::Checksum +## +## Revision 1.11 2004/05/11 20:03:48 jonen +## bugfix[joko] related to Attribute Map +## +## Revision 1.10 2003/06/25 23:03:57 joko +## no debugging +## +## Revision 1.9 2003/05/13 08:17:52 joko +## buildAttributeMap now propagates error +## +## Revision 1.8 2003/03/27 15:31:15 joko +## fixes to modules regarding new namespace(s) below Data::Mungle::* +## +## Revision 1.7 2003/02/21 08:01:11 joko +## debugging, logging +## renamed module +## +## Revision 1.6 2003/02/14 14:03:49 joko +## + logging, debugging +## - refactored code to sister module +## +## Revision 1.5 2003/02/11 05:30:47 joko +## + minor fixes and some debugging mud +## +## Revision 1.4 2003/02/09 05:01:10 joko +## + major structure changes +## - refactored code to sister modules +## ## Revision 1.3 2003/01/20 17:01:14 joko ## + cosmetics and debugging ## + probably refactored code to new plugin-modules 'Metadata.pm' and/or 'StorageInterface.pm' (guess it was the last one...) @@ -48,7 +81,7 @@ ## + minor cosmetics for logging ## ## Revision 1.2 2002/12/01 04:43:25 joko -## + mapping deatil entries may now be either an ARRAY or a HASH +## + mapping detail entries may now be either an ARRAY or a HASH ## + erase flag is used now (for export-operations) ## + expressions to refer to values inside deep nested structures ## - removed old mappingV2-code @@ -60,7 +93,7 @@ ## ## Revision 1.1 2002/10/10 03:44:21 cvsjoko ## + new -## ---------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------- package Data::Transfer::Sync::Core; @@ -75,13 +108,13 @@ use Data::Dumper; -use misc::HashExt; -use libp qw( md5_base64 ); -use libdb qw( quotesql hash2Sql ); -use Data::Transform::Deep qw( hash2object refexpr2perlref ); -use Data::Compare::Struct qw( getDifference isEmpty ); +#use misc::HashExt; +use Hash::Serializer; +use Data::Mungle::Compare::Struct qw( getDifference isEmpty ); +use Data::Mungle::Transform::Deep qw( deep_copy expand ); use Data::Storage::Container; use DesignPattern::Object; +use shortcuts::database qw( quotesql ); # get logger instance my $logger = Log::Dispatch::Config->instance; @@ -112,12 +145,18 @@ $self->{container}->addStorage($_, $self->{storages}->{$_}); } + # trace + #print Dumper($self); + #exit; + return 1; } sub _initV1 { my $self = shift; + $logger->debug( __PACKAGE__ . "->_initV1" ); + die("this should not be reached!"); # tag storages with id-authority and checksum-provider information # TODO: better store tag inside metadata to hold bits together! map { $self->{container}->{storage}->{$_}->{isIdentAuthority} = 1 } @{$self->{id_authorities}}; @@ -127,11 +166,17 @@ # TODO: abstract the hardwired use of "source" and "target" in here somehow - hmmmm....... /(="§/%??? -sub _syncNodes { +sub _run { my $self = shift; - my $tc = OneLineDumpHash->new( {} ); + #print "verbose: ", $self->{verbose}, "\n"; + $self->{verbose} = 1; + + $logger->debug( __PACKAGE__ . "->_run" ); + + # for statistics + my $tc = Hash::Serializer->new( {} ); my $results; # set of objects is already in $self->{args} @@ -150,30 +195,49 @@ # checkpoint: do we actually have a list to iterate through? if (!$results || !@{$results}) { - $logger->notice( __PACKAGE__ . "->syncNodes: No nodes to synchronize." ); + $logger->notice( __PACKAGE__ . "->_run: No nodes to synchronize." ); return; } + + #print Dumper(@$results); + #exit; + + # check if we actually *have* a synchronization method + if (!$self->{options}->{metadata}->{syncMethod}) { + $logger->critical( __PACKAGE__ . "->_run: No synchronization method (checksum|timestamp) specified" ); + return; + } + # dereference my @results = @{$results}; + #print Dumper(@results); # iterate through set foreach my $source_node_real (@results) { + print ":" if $self->{verbose}; + + #print Dumper($source_node_real); + $tc->{total}++; -#print "======================== iter", "\n"; + #print "=" x 80, "\n"; # clone object (in case we have to modify it here) # TODO: # - is a "deep_copy" needed here if occouring modifications take place? # - puuhhhh, i guess a deep_copy would destroy tangram mechanisms? # - after all, just take care for now that this object doesn't get updated! + # - so, just use its reference for now - if some cloning is needed in future, do this here! my $source_node = $source_node_real; + #my $source_node = expand($source_node_real); # modify entry - handle new style callbacks (the readers) -#print Dumper($source_node); -#exit; + + # trace + #print Dumper($source_node); + #exit; my $descent = 'source'; @@ -181,13 +245,22 @@ my $map_callbacks = {}; if (my $callbacks = $self->{meta}->{$descent}->{Callback}) { + # trace + #print Dumper($callbacks); + #exit; + my $error = 0; foreach my $node (keys %{$callbacks->{read}}) { + #print "cb_node: $node", "\n"; + my $object = $source_node; my $value; # = $source_node->{$node}; + # trace + #print Dumper($self->{options}); + # ------------ half-redundant: make $self->callCallback($object, $value, $opts) #my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_read'; my $perl_callback = $self->{meta}->{$descent}->{nodeType} . '::' . $node . '_read'; @@ -195,9 +268,9 @@ #print $evalstring, "\n"; exit; my $cb_result = eval($evalstring); if ($@) { - die $@; $error = 1; - print $@, "\n"; + $logger->error( __PACKAGE__ . "->_run: $@" ); + next; } # ------------ half-redundant: make $self->callCallback($object, $value, $opts) @@ -207,7 +280,8 @@ } -#print Dumper($source_node); + # trace + #print Dumper($source_node); # exclude defined fields (simply delete from object) map { delete $source_node->{$_} } @{$self->{meta}->{source}->{subnodes_exclude}}; @@ -216,17 +290,20 @@ $self->{node} = {}; $self->{node}->{source}->{payload} = $source_node; -#print "res - ident", "\n"; + # trace + #print Dumper($self->{node}); + #exit; # determine ident of entry my $identOK = $self->_resolveNodeIdent('source'); #if (!$identOK && lc $self->{args}->{direction} ne 'import') { if (!$identOK) { #print Dumper($self->{meta}->{source}); - $logger->critical( __PACKAGE__ . "->syncNodes: No ident found in source node ( nodeName='$self->{meta}->{source}->{nodeName}', nodeType='$self->{meta}->{source}->{nodeType}') try to \"prepare\" this node first?" ); + $logger->critical( __PACKAGE__ . "->_run: No ident found in source node ( nodeName='$self->{meta}->{source}->{nodeName}', nodeType='$self->{meta}->{source}->{nodeType}') try to \"prepare\" this node first?" ); return; } + #print "l" if $self->{verbose}; my $statOK = $self->_statloadNode('target', $self->{node}->{source}->{ident}); # mark node as new either if there's no ident or if stat/load failed @@ -235,32 +312,27 @@ print "n" if $self->{verbose}; } -#print "checksum", "\n"; - # determine status of entry by synchronization method - if ( (lc $self->{args}->{method} eq 'checksum') ) { + if ( lc $self->{options}->{metadata}->{syncMethod} eq 'checksum' ) { #if ( $statOK && (lc $self->{args}->{method} eq 'checksum') ) { #if ( !$self->{node}->{status}->{new} && (lc $self->{args}->{method} eq 'checksum') ) { - # TODO: - # is this really worth a "critical"??? - # no - it should just be a debug appendix i believe - -#print "readcs", "\n"; - - # calculate checksum of source node + # calculate local checksum of source node + $self->handleLocalChecksum('source'); + + # calculate checksum of source node #$self->_calcChecksum('source'); - if (!$self->_readChecksum('source')) { - $logger->critical( __PACKAGE__ . "->_readChecksum: Could not find \"source\" entry with ident=\"$self->{node}->{source}->{ident}\"" ); + if (!$self->readChecksum('source')) { + $logger->warning( __PACKAGE__ . "->_run: Could not find \"source\" entry with ident=\"$self->{node}->{source}->{ident}\"" ); $tc->{skip}++; print "s" if $self->{verbose}; next; } # get checksum from synchronization target - $self->_readChecksum('target'); - #if (!$self->_readChecksum('target')) { - # $logger->critical( __PACKAGE__ . "->_readChecksum: Could not find \"target\" entry with ident=\"$self->{node}->{source}->{ident}\"" ); + $self->readChecksum('target'); + #if (!$self->readChecksum('target')) { + # $logger->critical( __PACKAGE__ . "->readChecksum: Could not find \"target\" entry with ident=\"$self->{node}->{source}->{ident}\"" ); # next; #} @@ -273,6 +345,14 @@ # determine if entry is "new" or "dirty" # after all, this seems to be the point where the hammer falls..... print "c" if $self->{verbose}; + + # trace + #print Dumper($self->{node}); + #exit; + #print "LOCAL: ", $self->{node}->{source}->{checksum_local_storage}, " <-> ", $self->{node}->{source}->{checksum_local_calculated}, "\n"; + #print "REMOTE: ", $self->{node}->{source}->{checksum}, " <-> ", $self->{node}->{target}->{checksum}, "\n"; + + # calculate new/dirty status $self->{node}->{status}->{new} = !$self->{node}->{target}->{checksum}; if (!$self->{node}->{status}->{new}) { $self->{node}->{status}->{dirty} = @@ -282,6 +362,24 @@ $self->{args}->{force}; } + # new 2004-06-17: also check if local checksum is inconsistent + if ($self->{node}->{source}->{checksum_local_storage} ne $self->{node}->{source}->{checksum_local_calculated}) { + $self->{node}->{status}->{dirty_local} = 1; + $self->{node}->{status}->{dirty} = 1; + } + + } else { + $logger->warning( __PACKAGE__ . "->_run: Synchronization method '$self->{options}->{metadata}->{syncMethod}' is not implemented" ); + $tc->{skip}++; + print "s" if $self->{verbose}; + next; + } + + # new 2004-06-17: also update local checksum + if ($self->{node}->{status}->{dirty_local}) { + $tc->{locally_modified}++; + print "[lm]" if $self->{verbose}; + $self->_doModify_LocalChecksum('source'); } # first reaction on entry-status: continue with next entry if the current is already "in sync" @@ -290,25 +388,37 @@ next; } - # build map to actually transfer the data from source to target - $self->_buildMap(); - + #print Dumper($self->{node}->{source}); -#print Dumper($self->{node}); exit; + # build map to actually transfer the data from source to target + if (!$self->buildAttributeMap()) { + #$logger->warning( __PACKAGE__ . "->_run: Attribute Map could not be created. Will not insert or modify node."); + push( @{$tc->{error_per_row}}, "Attribute Map could not be created. Will not insert or modify node $self->{node}->{source}->{ident}."); + #push( @{$tc->{error_per_row}}, "Attribute Map could not be created. Will not insert or modify node " . Dumper($self->{node}->{source}) . "."); + $tc->{error}++; + print "e" if $self->{verbose}; + next; + } -#print "attempt", "\n"; + # trace + #print Dumper($self->{node}); exit; + #print "attempt", "\n"; # additional (new) checks for feature "write-protection" if ($self->{meta}->{target}->{storage}->{isWriteProtected}) { $tc->{attempt_transfer}++; print "\n" if $self->{verbose}; - $logger->notice( __PACKAGE__ . "->syncNodes: Target is write-protected. Will not insert or modify node. " . + $logger->notice( __PACKAGE__ . "->_run: Target is write-protected. Will not insert or modify node. " . "(Ident: $self->{node}->{source}->{ident} " . "Dump:\n" . Dumper($self->{node}->{source}->{payload}) . ")" ); print "\n" if $self->{verbose}; $tc->{skip}++; next; } + # trace + #print Dumper($self); + #exit; + # transfer contents of map to target if ($self->{node}->{status}->{new}) { $tc->{attempt_new}++; @@ -316,14 +426,14 @@ # asymmetry: refetch node from target to re-calculate new ident and checksum (TODO: is IdentAuthority of relevance here?) #print Dumper($self->{node}); $self->_statloadNode('target', $self->{node}->{target}->{ident}, 1); - $self->_readChecksum('target'); + $self->readChecksum('target'); } elsif ($self->{node}->{status}->{dirty}) { $tc->{attempt_modify}++; # asymmetry: get ident before updating (TODO: is IdentAuthority of relevance here?) $self->{node}->{target}->{ident} = $self->{node}->{map}->{$self->{meta}->{target}->{IdentProvider}->{arg}}; $self->_doTransferToTarget('update'); - $self->_readChecksum('target'); + $self->readChecksum('target'); } if ($self->{node}->{status}->{ok}) { @@ -337,17 +447,23 @@ print "e" if $self->{verbose}; } + # trace + #print Dumper($self); + #exit; + # change ident in source (take from target), if transfer was ok and target is an IdentAuthority # this is (for now) called a "retransmit" indicated by a "r"-character when verbosing - if ($self->{node}->{status}->{ok} && $self->{meta}->{target}->{storage}->{isIdentAuthority}) { + #if ($self->{node}->{status}->{ok} && $self->{options}->{target}->{storage}->{idAuthority}) { + if ($self->{node}->{status}->{ok} && $self->{meta}->{target}->{isIdentAuthority}) { print "r" if $self->{verbose}; #print Dumper($self->{meta}); #print Dumper($self->{node}); #exit; $self->_doModifySource_IdentChecksum($self->{node}->{target}->{ident}); } - - print ":" if $self->{verbose}; + + #print "UNLOAD", "\n"; + #$self->{meta}->{source}->{storage}->unload( $self->{node}->{source}->{payload} ); } @@ -364,452 +480,27 @@ # todo!!! #sysevent( { usermsg => $msg, level => $level }, $taskEvent ); - $logger->info( __PACKAGE__ . "->syncNodes: $msg" ); + #$logger->info( __PACKAGE__ . "->_run: $msg" ); + $logger->info($msg . "\n"); return $tc; } -# refactor this as some core-function to do a generic dump resolving data-encapsulations of e.g. Set::Object -sub _dumpCompact { - my $self = shift; - - #my $vars = \@_; - my @data = (); - - my $count = 0; - foreach (@_) { - my $item = {}; - foreach my $key (keys %$_) { - my $val = $_->{$key}; - -#print Dumper($val); - - if (ref $val eq 'Set::Object') { - #print "========================= SET", "\n"; -#print Dumper($val); - #print Dumper($val->members()); - #$val = $val->members(); - #$vars->[$count]->{$key} = $val->members() if $val->can("members"); - #$item->{$key} = $val->members() if $val->can("members"); - $item->{$key} = $val->members(); - #print Dumper($vars->[$count]->{$key}); - - } else { - $item->{$key} = $val; - } - - } - push @data, $item; - $count++; - } - -#print "Dump:", Dumper(@data), "\n"; - - $Data::Dumper::Indent = 0; - my $result = Dumper(@data); - $Data::Dumper::Indent = 2; - return $result; - -} - - -sub _calcChecksum { - - my $self = shift; - my $descent = shift; - my $specifier = shift; - - # calculate checksum for current object - my $ident = $self->{node}->{$descent}->{ident}; - - # build dump of this node - my $payload = $self->{node}->{$descent}->{payload}; - #my $dump = $ident . "\n" . $item->quickdump(); - #my $dump = $ident . "\n" . Dumper($item); - my $dump = $ident . "\n" . $self->_dumpCompact($payload); - - # TODO: $logger->dump( ... ); - #$logger->debug( __PACKAGE__ . ": " . $dump ); - #$logger->dump( __PACKAGE__ . ": " . $dump ); - - # calculate checksum from dump - # note: the 32-bit integer hash from DBI seems - # to generate duplicates with small payloads already in ranges of hundreds of items/rows!!! - # try to avoid to use it or try to use it only for payloads greater than, hmmm, let's say 30 chars? - # (we had about 15 chars average per item (row)) - - # md5-based fingerprint, base64 encoded (from Digest::MD5) - $self->{node}->{$descent}->{checksum} = md5_base64($dump) . '=='; - # 32-bit integer "hash" value (maybe faster?) (from DBI) - #$self->{node}->{$descent}->{checksum} = DBI::hash($dump, 1); - - # signal good - return 1; - -} - - -sub _readChecksum { - my $self = shift; - - my $descent = shift; - - #print "getcheck:", "\n"; print Dumper($self->{node}->{$descent}); - - if (!$self->{node}->{$descent}) { - # signal checksum bad - return; - } - - # get checksum for current entry - # TODO: don't have the checksum column/property hardcoded as "cs" here, make this configurable somehow - - if ($self->{meta}->{$descent}->{storage}->{isChecksumAuthority}) { - #$self->{node}->{$descent}->{checksum} = $entry->{cs}; - #$self->{node}->{$descent}->{checksum} = $self->_calcChecksum($descent); # $entry->{cs}; - #print "descent: $descent", "\n"; - $self->_calcChecksum($descent); - #print "checksum: ", $self->{node}->{$descent}->{checksum}, "\n"; - } else { - - #$self->{node}->{$descent}->{checksum} = $entry->{cs}; - $self->{node}->{$descent}->{checksum} = $self->{node}->{$descent}->{payload}->{cs}; - } - - # signal checksum good - return 1; - -} - - -sub _buildMap { - - my $self = shift; - - # field-structure for building sql - # mapping of sql-fieldnames to object-attributes - $self->{node}->{map} = {}; - - # manually set ... - # ... object-id - $self->{node}->{map}->{$self->{meta}->{target}->{IdentProvider}->{arg}} = $self->{node}->{source}->{ident}; - # ... checksum - $self->{node}->{map}->{cs} = $self->{node}->{source}->{checksum}; - -#print "sqlmap: ", Dumper($self->{node}->{map}), "\n"; - - # for transferring flat structures via simple (1:1) mapping - # TODO: diff per property / property value - - if ($self->{args}->{mapping}) { - # apply mapping from $self->{args}->{mapping} to $self->{node}->{map} - #foreach my $key (@{$self->{meta}->{source}->{childnodes}}) { - my @childnodes = @{$self->{meta}->{source}->{childnodes}}; - for (my $mapidx = 0; $mapidx <= $#childnodes; $mapidx++) { - #my $map_right = $self->{args}->{mapping}->{$key}; - - $self->{node}->{source}->{propcache} = {}; - $self->{node}->{target}->{propcache} = {}; - - # get property name - $self->{node}->{source}->{propcache}->{property} = $self->{meta}->{source}->{childnodes}->[$mapidx]; - $self->{node}->{target}->{propcache}->{property} = $self->{meta}->{target}->{childnodes}->[$mapidx]; - #print "map: $map_right", "\n"; - - # get property value - my $value; - - # detect for callback - old style - (maybe the better???) - if (ref($self->{node}->{target}->{map}) eq 'CODE') { - #$value = &$map_right($objClone); - } else { - # plain (scalar?) value - #$value = $objClone->{$map_right}; - $self->{node}->{source}->{propcache}->{value} = $self->{node}->{source}->{payload}->{$self->{node}->{source}->{propcache}->{property}}; - } - #$self->{node}->{map}->{$key} = $value; - - # detect expression - # for transferring deeply nested structures described by expressions - #print "val: $self->{node}->{source}->{propcache}->{value}", "\n"; - if ($self->{node}->{source}->{propcache}->{property} =~ s/^expr://) { - - # create an anonymous sub to act as callback target dispatcher - my $cb_dispatcher = sub { - #print "=============== CALLBACK DISPATCHER", "\n"; - #print "ident: ", $self->{node}->{source}->{ident}, "\n"; - #return $self->{node}->{source}->{ident}; - - }; - - -#print Dumper($self->{node}); - - # build callback map for helper function - #my $cbmap = { $self->{meta}->{source}->{IdentProvider}->{arg} => $cb_dispatcher }; - my $cbmap = {}; - my $value = refexpr2perlref($self->{node}->{source}->{payload}, $self->{node}->{source}->{propcache}->{property}, $cbmap); - $self->{node}->{source}->{propcache}->{value} = $value; - } - - # encode values dependent on type of underlying storage here - expand cases... - my $storage_type = $self->{meta}->{target}->{storage}->{locator}->{type}; - if ($storage_type eq 'DBI') { - # ...for sql - $self->{node}->{source}->{propcache}->{value} = quotesql($self->{node}->{source}->{propcache}->{value}); - } - elsif ($storage_type eq 'Tangram') { - # iso? utf8 already possible? - - } elsif ($storage_type eq 'LDAP') { - # TODO: encode utf8 here? - } - - # store value to transfer map - $self->{node}->{map}->{$self->{node}->{target}->{propcache}->{property}} = $self->{node}->{source}->{propcache}->{value}; - - } - } - - - # TODO: $logger->dump( ... ); - #$logger->debug( "sqlmap:" . "\n" . Dumper($self->{node}->{map}) ); -#print "sqlmap: ", Dumper($self->{node}->{map}), "\n"; -#print "entrystatus: ", Dumper($self->{node}), "\n"; - -} - - - -sub _modifyNode { +sub _doTransferToTarget { my $self = shift; - my $descent = shift; my $action = shift; - my $map = shift; - my $crit = shift; - - # map for new style callbacks - my $map_callbacks = {}; - - # checks go first! - - # TODO: this should be reviewed first - before extending ;-) - # TODO: this should be extended: - # count this cases inside the caller to this sub and provide a better overall message - # if this counts still zero in the end: - # "No nodes have been touched for modify: Do you have column-headers in your csv file?" - if (not defined $self->{node}) { - #$logger->critical( __PACKAGE__ . "->_modifyNode failed: \"$descent\" node is empty." ); - #return; - } - - # transfer callback nodes from value map to callback map - handle them afterwards! - (new style callbacks) - if (my $callbacks = $self->{meta}->{$descent}->{Callback}) { - foreach my $callback (keys %{$callbacks->{write}}) { - $map_callbacks->{write}->{$callback} = $map->{$callback}; - delete $map->{$callback}; - } - } - - - #print Dumper($self->{meta}); - - # DBI speaks SQL - if ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'DBI') { - -#print Dumper($self->{node}); - my $sql_main; - # translate map to sql - #print $action, "\n"; exit; - #print $self->{meta}->{$descent}->{node}, "\n"; exit; - #print "action:"; - #print $action, "\n"; -#$action = "anc"; -#print "yai", "\n"; - -#print Dumper($map); -#delete $map->{cs}; - - if (lc($action) eq 'insert') { - $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_INSERT'); - } elsif (lc $action eq 'update') { - $crit ||= "$self->{meta}->{$descent}->{IdentProvider}->{arg}='$self->{node}->{$descent}->{ident}'"; - $sql_main = hash2Sql($self->{meta}->{$descent}->{node}, $map, 'SQL_UPDATE', $crit); - } - -#$sql_main = "UPDATE currencies_csv SET oid='abcdef' WHERE text='Australian Dollar' AND key='AUD';"; -#$sql_main = "UPDATE currencies_csv SET oid='huhu2' WHERE ekey='AUD'"; - -#print "sql: ", $sql_main, "\n"; -#exit; - - # transfer data - my $sqlHandle = $self->{meta}->{$descent}->{storage}->sendCommand($sql_main); - -#exit; - - # handle errors - if ($sqlHandle->err) { - #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; } - $self->{node}->{status}->{error} = { - statement => $sql_main, - state => $sqlHandle->state, - err => $sqlHandle->err, - errstr => $sqlHandle->errstr, - }; - } else { - $self->{node}->{status}->{ok} = 1; - } - - # Tangram does it the oo-way (naturally) - } elsif ($self->{meta}->{$descent}->{storage}->{locator}->{type} eq 'Tangram') { - my $sql_main; - my $object; - - # determine classname - my $classname = $self->{meta}->{$descent}->{node}; - - # properties to exclude - my @exclude = @{$self->{meta}->{$descent}->{subnodes_exclude}}; - - - if (my $identProvider = $self->{meta}->{$descent}->{IdentProvider}) { - push @exclude, $identProvider->{arg}; - } - - # new feature: - # - check TypeProvider metadata property from other side - # - use argument (arg) inside as a classname for object creation on this side - #my $otherSide = $self->_otherSide($descent); - if (my $typeProvider = $self->{meta}->{$descent}->{TypeProvider}) { - #print Dumper($map); - $classname = $map->{$typeProvider->{arg}}; - # remove nodes from map also (push nodes to "subnodes_exclude" list) - push @exclude, $typeProvider->{arg}; - } - # exclude banned properties (remove from map) - #map { delete $self->{node}->{map}->{$_} } @{$self->{args}->{exclude}}; - map { delete $map->{$_} } @exclude; - - # list of properties - my @props = keys %{$map}; + # trace + #print Dumper($self->{meta}); + #print Dumper($self->{node}); + #exit; - # transfer data - if (lc $action eq 'insert') { - - # build array to initialize object - #my @initarray = (); - #map { push @initarray, $_, undef; } @props; - - # make the object persistent in four steps: - # - raw create (perl / class tangram scope) - # - engine insert (tangram scope) ... this establishes inheritance - don't try to fill in inherited properties before! - # - raw fill-in from hash (perl scope) - # - engine update (tangram scope) ... this updates all properties just filled in - - # create new object ... - #my $object = $classname->new( @initarray ); - $object = $classname->new(); - - # ... pass to orm ... - $self->{meta}->{$descent}->{storage}->insert($object); - - # ... and initialize with empty (undef'd) properties. - #print Dumper(@props); - map { $object->{$_} = undef; } @props; - - # mix in values ... - hash2object($object, $map); - - # ... and re-update@orm. -#print Dumper($object); - $self->{meta}->{$descent}->{storage}->update($object); - - # asymmetry: get ident after insert - # TODO: - # - just do this if it is an IdentAuthority - # - use IdentProvider metadata here -#print Dumper($self->{meta}->{$descent}); - my $oid = $self->{meta}->{$descent}->{storage}->id($object); -#print "oid: $oid", "\n"; - $self->{node}->{$descent}->{ident} = $oid; - - - } elsif (lc $action eq 'update') { - - # get fresh object from orm first - $object = $self->{meta}->{$descent}->{storage}->load($self->{node}->{$descent}->{ident}); - -#print Dumper($self->{node}); - - # mix in values - #print Dumper($object); - hash2object($object, $map); - #print Dumper($object); - #exit; - $self->{meta}->{$descent}->{storage}->update($object); - } - - my $error = 0; - - # handle new style callbacks - this is a HACK - do this without an eval! - #print Dumper($map); - #print "cb: ", Dumper($self->{meta}->{$descent}->{Callback}); - #print Dumper($map_callbacks); - foreach my $node (keys %{$map_callbacks->{write}}) { - #print Dumper($node); - my $perl_callback = $self->{meta}->{$descent}->{node} . '::' . $node . '_write'; - my $evalstring = $perl_callback . '( { object => $object, value => $map_callbacks->{write}->{$node}, storage => $self->{meta}->{$descent}->{storage} } );'; - #print $evalstring, "\n"; exit; - eval($evalstring); - if ($@) { - $error = 1; - print $@, "\n"; - } - - #print "after eval", "\n"; - - if (!$error) { - # re-update@orm - $self->{meta}->{$descent}->{storage}->update($object); - } - } - - # handle errors - if ($error) { - #print "error", "\n"; -=pod - my $sqlHandle; - #if ($self->{args}->{debug}) { print "sql-error with statement: $sql_main", "\n"; } - $self->{node}->{status}->{error} = { - statement => $sql_main, - state => $sqlHandle->state, - err => $sqlHandle->err, - errstr => $sqlHandle->errstr, - }; -=cut - # rollback.... - #print "rollback", "\n"; - $self->{meta}->{$descent}->{storage}->erase($object); - #print "after rollback", "\n"; - } else { - $self->{node}->{status}->{ok} = 1; - } - - - } - -} - -sub _doTransferToTarget { - my $self = shift; - my $action = shift; $self->_modifyNode('target', $action, $self->{node}->{map}); } + sub _doModifySource_IdentChecksum { my $self = shift; my $ident_new = shift; @@ -829,7 +520,14 @@ $self->_modifyNode('source', 'update', $map); } - +sub _doModify_LocalChecksum { + my $self = shift; + my $descent = shift; + my $map = { + cs_local => $self->{node}->{$descent}->{checksum_local_calculated}, + }; + $self->_modifyNode($descent, 'update', $map); +} sub _prepareNode_MetaProperties { my $self = shift; @@ -865,10 +563,12 @@ } +# TODO: load column-metadata from reversed mapping-metadata sub _prepareNode_DummyIdent { my $self = shift; my $descent = shift; + #print Dumper($self->{options}); $logger->info( __PACKAGE__ . "->_prepareNode_DummyIdent( descent $descent )" ); my $list = $self->_getNodeList($descent); @@ -882,6 +582,7 @@ my $map = { $self->{meta}->{$descent}->{IdentProvider}->{arg} => $ident_dummy, cs => undef, + cs_local => undef, }; # diff lists and ... @@ -923,12 +624,6 @@ return ''; } -sub _erase_all { - my $self = shift; - my $descent = shift; - #my $node = shift; - my $node = $self->{meta}->{$descent}->{node}; - $self->{meta}->{$descent}->{storage}->eraseAll($node); -} 1; +__END__