[Bio] / Sprout / ERDB.pm Repository:
ViewVC logotype

Diff of /Sprout/ERDB.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.31, Thu Jan 19 09:28:11 2006 UTC revision 1.32, Sat Jan 28 08:58:53 2006 UTC
# Line 1051  Line 1051 
1051      return $retVal;      return $retVal;
1052  }  }
1053    
1054    =head3 Delete
1055    
1056    C<< my $stats = $erdb->Delete($entityName, $objectID); >>
1057    
1058    Delete an entity instance from the database. The instance is deleted along with all entity and
1059    relationship instances dependent on it. The idea of dependence here is recursive. An object is
1060    always dependent on itself. An object is dependent if it is a 1-to-many or many-to-many
1061    relationship connected to a dependent entity or the "to" entity connected to a 1-to-many
1062    dependent relationship.
1063    
1064    =over 4
1065    
1066    =item entityName
1067    
1068    Name of the entity type for the instance being deleted.
1069    
1070    =item objectID
1071    
1072    ID of the entity instance to be deleted. If the ID contains a wild card character (C<%>),
1073    then it is presumed to by a LIKE pattern.
1074    
1075    =item testFlag
1076    
1077    If TRUE, the delete statements will be traced without being executed.
1078    
1079    =item RETURN
1080    
1081    Returns a statistics object indicating how many records of each particular table were
1082    deleted.
1083    
1084    =back
1085    
1086    =cut
1087    #: Return Type $%;
1088    sub Delete {
1089        # Get the parameters.
1090        my ($self, $entityName, $objectID, $testFlag) = @_;
1091        # Declare the return variable.
1092        my $retVal = Stats->new();
1093        # Get the DBKernel object.
1094        my $db = $self->{_dbh};
1095        # We're going to generate all the paths branching out from the starting entity. One of
1096        # the things we have to be careful about is preventing loops. We'll use a hash to
1097        # determine if we've hit a loop.
1098        my %alreadyFound = ();
1099        # This next list will serve as our result stack. We start by pushing object lists onto
1100        # the stack, and then popping them off to do the deletes. This means the deletes will
1101        # start with the longer paths before getting to the shorter ones. That, in turn, makes
1102        # sure we don't delete records that might be needed to forge relationships back to the
1103        # original item.
1104        my @pathList = ();
1105        # This final hash is used to remember what work still needs to be done. We push paths
1106        # onto the list, then pop them off to extend the paths. We prime it with the starting
1107        # point. Note that we will work hard to insure that the last item on a path in the
1108        # TODO list is always an entity.
1109        my @todoList = ([$entityName]);
1110        while (@todoList) {
1111            # Get the current path.
1112            my $current = pop @todoList;
1113            # Copy it into a list.
1114            my @stackedPath = @{$current};
1115            # Pull off the last item on the path. It will always be an entity.
1116            my $entityName = pop @stackedPath;
1117            # Add it to the alreadyFound list.
1118            $alreadyFound{$entityName} = 1;
1119            # Get the entity data.
1120            my $entityData = $self->_GetStructure($entityName);
1121            # The first task is to loop through the entity's relation. A DELETE command will
1122            # be needed for each of them.
1123            my $relations = $entityData->{Relations};
1124            for my $relation (keys %{$relations}) {
1125                my @augmentedList = (@stackedPath, $relation);
1126                push @pathList, \@augmentedList;
1127            }
1128            # Now we need to look for relationships connected to this entity.
1129            my $relationshipList = $self->{_metaData}->{Relationships};
1130            for my $relationshipName (keys %{$relationshipList}) {
1131                my $relationship = $relationshipList->{$relationshipName};
1132                # Check the FROM field. We're only interested if it's us.
1133                if ($relationship->{from} eq $entityName) {
1134                    # Add the path to this relationship.
1135                    my @augmentedList = (@stackedPath, $entityName, $relationshipName);
1136                    push @pathList, \@augmentedList;
1137                    # Check the arity. If it's MM we're done. If it's 1M
1138                    # and the target hasn't been seen yet, we want to
1139                    # stack the entity for future processing.
1140                    if ($relationship->{arity} eq '1M') {
1141                        my $toEntity = $relationship->{to};
1142                        if (! exists $alreadyFound{$toEntity}) {
1143                            # Here we have a new entity that's dependent on
1144                            # the current entity, so we need to stack it.
1145                            my @stackList = (@augmentedList, $toEntity);
1146                            push @pathList, \@stackList;
1147                        }
1148                    }
1149                }
1150                # Now check the TO field. In this case only the relationship needs
1151                # deletion.
1152                if ($relationship->{to} eq $entityName) {
1153                    my @augmentedList = (@stackedPath, $entityName, $relationshipName);
1154                    push @pathList, \@augmentedList;
1155                }
1156            }
1157        }
1158        # Create the first qualifier for the WHERE clause. This selects the
1159        # keys of the primary entity records to be deleted. When we're deleting
1160        # from a dependent table, we construct a join page from the first qualifier
1161        # to the table containing the dependent records to delete.
1162        my $qualifier = ($objectID =~ /%/ ? "LIKE ?" : "= ?");
1163        # Now it's time to do the deletes. We simply pop the paths off the stack.
1164        while (my $path = pop @pathList) {
1165            # Get the table whose rows are to be deleted.
1166            my @pathTables = @{$path};
1167            # Start the DELETE statement.
1168            my $target = $pathTables[$#pathTables];
1169            my $stmt = "DELETE FROM $target";
1170            # If there's more than just the one table, we need a USING clause.
1171            if (@pathTables > 1) {
1172                $stmt .= " USING " . join(", ", @pathTables[0 .. ($#pathTables - 1)]);
1173            }
1174            # Now start the WHERE. The first thing is the ID field from the starting table. That
1175            # starting table will either be the entity relation or one of the entity's
1176            # sub-relations.
1177            $stmt .= " WHERE $pathTables[0].id $qualifier";
1178            # Now we run through the remaining entities in the path, connecting them up.
1179            for (my $i = 1; $i <= $#pathTables; $i += 2) {
1180                # Connect the current relationship to the preceding entity.
1181                my ($entity, $rel) = @pathTables[$i-1,$i];
1182                # The style of connection depends on the direction of the relationship.
1183                # We compute the direction by checking whether the preceding entity is
1184                # the FROM or TO entity.
1185                my $relationship = $self->_GetStructure($rel);
1186                if ($relationship->{to} eq $entity) {
1187                    # Here we're the TO. A TO link is always the end of a chain, so
1188                    # we just tack it on at the end.
1189                    $stmt .= " AND $entity.id = $rel.to_link";
1190                } else {
1191                    # Here we're the FROM. In that case, we'll need to check for a
1192                    # next entity.
1193                    $stmt .= " AND $entity.id = $rel.from_link";
1194                    if ($i + 1 <= $#pathTables) {
1195                        # Here there's a next entity, so connect that to the relationship's
1196                        # to-link.
1197                        my $entity2 = $pathTables[$i+1];
1198                        $stmt .= " AND $rel.to_link = $entity2.id";
1199                    }
1200                }
1201            }
1202            # Now we have our desired DELETE statement.
1203            if ($testFlag) {
1204                # Here the user wants to trace without executing.
1205                Trace($stmt) if T(0);
1206            } else {
1207                # Here we can delete. Note that the SQL method dies with a confessing
1208                # if an error occurs, so we just go ahead and do it.
1209                Trace("Executing delete: $stmt") if T(3);
1210                my $rv = $db->SQL($stmt, 0, [$objectID]);
1211                # Accumulate the statistics for this delete. The only rows deleted
1212                # are from the target table, so we use its name to record the
1213                # statistic.
1214                $retVal->Add($target, $rv);
1215            }
1216        }
1217        # Return the result.
1218        return $retVal;
1219    }
1220    
1221  =head3 GetList  =head3 GetList
1222    
1223  C<< my @dbObjects = $erdb->GetList(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>  C<< my @dbObjects = $erdb->GetList(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>

Legend:
Removed from v.1.31  
changed lines
  Added in v.1.32

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3