[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.11, Thu Jun 23 21:24:49 2005 UTC revision 1.19, Fri Sep 9 14:50:58 2005 UTC
# Line 2  Line 2 
2    
3      use strict;      use strict;
4      use Tracer;      use Tracer;
5      use DBKernel;      use DBrtns;
6      use Data::Dumper;      use Data::Dumper;
7      use XML::Simple;      use XML::Simple;
8      use DBQuery;      use DBQuery;
9      use DBObject;      use DBObject;
10      use Stats;      use Stats;
11      use Time::HiRes qw(gettimeofday);      use Time::HiRes qw(gettimeofday);
12        use FIG;
13    
14  =head1 Entity-Relationship Database Package  =head1 Entity-Relationship Database Package
15    
# Line 300  Line 301 
301  # Table of information about our datatypes. "sqlType" is the corresponding SQL datatype string.  # Table of information about our datatypes. "sqlType" is the corresponding SQL datatype string.
302  # "maxLen" is the maximum permissible length of the incoming string data used to populate a field  # "maxLen" is the maximum permissible length of the incoming string data used to populate a field
303  # of the specified type. "dataGen" is PERL string that will be evaluated if no test data generation  # of the specified type. "dataGen" is PERL string that will be evaluated if no test data generation
304   #string is specified in the field definition.  # string is specified in the field definition. "avgLen" is the average byte length for estimating
305  my %TypeTable = ( char =>    { sqlType => 'CHAR(1)',            maxLen => 1,            dataGen => "StringGen('A')" },  # record sizes.
306                    int =>     { sqlType => 'INTEGER',            maxLen => 20,           dataGen => "IntGen(0, 99999999)" },  my %TypeTable = ( char =>    { sqlType => 'CHAR(1)',            maxLen => 1,            avgLen =>   1, dataGen => "StringGen('A')" },
307                    string =>  { sqlType => 'VARCHAR(255)',       maxLen => 255,          dataGen => "StringGen(IntGen(10,250))" },                    int =>     { sqlType => 'INTEGER',            maxLen => 20,           avgLen =>   4, dataGen => "IntGen(0, 99999999)" },
308                    text =>    { sqlType => 'TEXT',               maxLen => 1000000000,   dataGen => "StringGen(IntGen(80,1000))" },                    string =>  { sqlType => 'VARCHAR(255)',       maxLen => 255,          avgLen => 100, dataGen => "StringGen(IntGen(10,250))" },
309                    date =>    { sqlType => 'BIGINT',             maxLen => 80,           dataGen => "DateGen(-7, 7, IntGen(0,1400))" },                    text =>    { sqlType => 'TEXT',               maxLen => 1000000000,   avgLen => 500, dataGen => "StringGen(IntGen(80,1000))" },
310                    float =>   { sqlType => 'DOUBLE PRECISION',   maxLen => 40,           dataGen => "FloatGen(0.0, 100.0)" },                    date =>    { sqlType => 'BIGINT',             maxLen => 80,           avgLen =>   8, dataGen => "DateGen(-7, 7, IntGen(0,1400))" },
311                    boolean => { sqlType => 'SMALLINT',           maxLen => 1,            dataGen => "IntGen(0, 1)" },                    float =>   { sqlType => 'DOUBLE PRECISION',   maxLen => 40,           avgLen =>   8, dataGen => "FloatGen(0.0, 100.0)" },
312                      boolean => { sqlType => 'SMALLINT',           maxLen => 1,            avgLen =>   2, dataGen => "IntGen(0, 1)" },
313                   'key-string' =>                   'key-string' =>
314                               { sqlType => 'VARCHAR(40)',        maxLen => 40,           dataGen => "StringGen(IntGen(10,40))" },                               { sqlType => 'VARCHAR(40)',        maxLen => 40,           avgLen =>  10, dataGen => "StringGen(IntGen(10,40))" },
315                   'name-string' =>                   'name-string' =>
316                               { sqlType => 'VARCHAR(80)',        maxLen => 80,           dataGen => "StringGen(IntGen(10,80))" },                               { sqlType => 'VARCHAR(80)',        maxLen => 80,           avgLen =>  40, dataGen => "StringGen(IntGen(10,80))" },
317                   'medium-string' =>                   'medium-string' =>
318                               { sqlType => 'VARCHAR(160)',       maxLen => 160,          dataGen => "StringGen(IntGen(10,160))" },                               { sqlType => 'VARCHAR(160)',       maxLen => 160,          avgLen =>  40, dataGen => "StringGen(IntGen(10,160))" },
319                  );                  );
320    
321  # Table translating arities into natural language.  # Table translating arities into natural language.
# Line 369  Line 371 
371    
372  =head3 ShowMetaData  =head3 ShowMetaData
373    
374  C<< $database->ShowMetaData($fileName); >>  C<< $erdb->ShowMetaData($fileName); >>
375    
376  This method outputs a description of the database. This description can be used to help users create  This method outputs a description of the database. This description can be used to help users create
377  the data to be loaded into the relations.  the data to be loaded into the relations.
# Line 524  Line 526 
526    
527  =head3 DumpMetaData  =head3 DumpMetaData
528    
529  C<< $database->DumpMetaData(); >>  C<< $erdb->DumpMetaData(); >>
530    
531  Return a dump of the metadata structure.  Return a dump of the metadata structure.
532    
# Line 539  Line 541 
541    
542  =head3 CreateTables  =head3 CreateTables
543    
544  C<< $datanase->CreateTables(); >>  C<< $erdb->CreateTables(); >>
545    
546  This method creates the tables for the database from the metadata structure loaded by the  This method creates the tables for the database from the metadata structure loaded by the
547  constructor. It is expected this function will only be used on rare occasions, when the  constructor. It is expected this function will only be used on rare occasions, when the
# Line 577  Line 579 
579    
580  =head3 CreateTable  =head3 CreateTable
581    
582  C<< $database->CreateTable($tableName, $indexFlag); >>  C<< $erdb->CreateTable($tableName, $indexFlag, $estimatedRows); >>
583    
584  Create the table for a relation and optionally create its indexes.  Create the table for a relation and optionally create its indexes.
585    
# Line 587  Line 589 
589    
590  Name of the relation (which will also be the table name).  Name of the relation (which will also be the table name).
591    
592  =item $indexFlag  =item indexFlag
593    
594  TRUE if the indexes for the relation should be created, else FALSE. If FALSE,  TRUE if the indexes for the relation should be created, else FALSE. If FALSE,
595  L</CreateIndexes> must be called later to bring the indexes into existence.  L</CreateIndexes> must be called later to bring the indexes into existence.
596    
597    =item estimatedRows (optional)
598    
599    If specified, the estimated maximum number of rows for the relation. This
600    information allows the creation of tables using storage engines that are
601    faster but require size estimates, such as MyISAM.
602    
603  =back  =back
604    
605  =cut  =cut
606    
607  sub CreateTable {  sub CreateTable {
608      # Get the parameters.      # Get the parameters.
609      my ($self, $relationName, $indexFlag) = @_;      my ($self, $relationName, $indexFlag, $estimatedRows) = @_;
610      # Get the database handle.      # Get the database handle.
611      my $dbh = $self->{_dbh};      my $dbh = $self->{_dbh};
612      # Get the relation data and determine whether or not the relation is primary.      # Get the relation data and determine whether or not the relation is primary.
# Line 622  Line 630 
630      # Insure the table is not already there.      # Insure the table is not already there.
631      $dbh->drop_table(tbl => $relationName);      $dbh->drop_table(tbl => $relationName);
632      Trace("Table $relationName dropped.") if T(2);      Trace("Table $relationName dropped.") if T(2);
633        # If there are estimated rows, create an estimate so we can take advantage of
634        # faster DB technologies.
635        my $estimation = undef;
636        if ($estimatedRows) {
637            $estimation = [$self->EstimateRowSize($relationName), $estimatedRows];
638        }
639      # Create the table.      # Create the table.
640      Trace("Creating table $relationName: $fieldThing") if T(2);      Trace("Creating table $relationName: $fieldThing") if T(2);
641      $dbh->create_table(tbl => $relationName, flds => $fieldThing);      $dbh->create_table(tbl => $relationName, flds => $fieldThing, estimates => $estimation);
642      Trace("Relation $relationName created in database.") if T(2);      Trace("Relation $relationName created in database.") if T(2);
643      # If we want to build the indexes, we do it here.      # If we want to build the indexes, we do it here.
644      if ($indexFlag) {      if ($indexFlag) {
# Line 634  Line 648 
648    
649  =head3 CreateIndex  =head3 CreateIndex
650    
651  C<< $database->CreateIndex($relationName); >>  C<< $erdb->CreateIndex($relationName); >>
652    
653  Create the indexes for a relation. If a table is being loaded from a large source file (as  Create the indexes for a relation. If a table is being loaded from a large source file (as
654  is the case in L</LoadTable>), it is best to create the indexes after the load. If that is  is the case in L</LoadTable>), it is sometimes best to create the indexes after the load.
655  the case, then L</CreateTable> should be called with the index flag set to FALSE, and this  If that is the case, then L</CreateTable> should be called with the index flag set to
656  method used after the load to create the indexes for the table.  FALSE, and this method used after the load to create the indexes for the table.
657    
658  =cut  =cut
659    
# Line 667  Line 681 
681    
682  =head3 LoadTables  =head3 LoadTables
683    
684  C<< my $stats = $database->LoadTables($directoryName, $rebuild); >>  C<< my $stats = $erdb->LoadTables($directoryName, $rebuild); >>
685    
686  This method will load the database tables from a directory. The tables must already have been created  This method will load the database tables from a directory. The tables must already have been created
687  in the database. (This can be done by calling L</CreateTables>.) The caller passes in a directory name;  in the database. (This can be done by calling L</CreateTables>.) The caller passes in a directory name;
# Line 737  Line 751 
751    
752  =head3 GetTableNames  =head3 GetTableNames
753    
754  C<< my @names = $database->GetTableNames; >>  C<< my @names = $erdb->GetTableNames; >>
755    
756  Return a list of the relations required to implement this database.  Return a list of the relations required to implement this database.
757    
# Line 754  Line 768 
768    
769  =head3 GetEntityTypes  =head3 GetEntityTypes
770    
771  C<< my @names = $database->GetEntityTypes; >>  C<< my @names = $erdb->GetEntityTypes; >>
772    
773  Return a list of the entity type names.  Return a list of the entity type names.
774    
# Line 771  Line 785 
785    
786  =head3 Get  =head3 Get
787    
788  C<< my $query = $database->Get(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>  C<< my $query = $erdb->Get(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>
789    
790  This method returns a query object for entities of a specified type using a specified filter.  This method returns a query object for entities of a specified type using a specified filter.
791  The filter is a standard WHERE/ORDER BY clause with question marks as parameter markers and each  The filter is a standard WHERE/ORDER BY clause with question marks as parameter markers and each
# Line 779  Line 793 
793  following call requests all B<Genome> objects for the genus specified in the variable  following call requests all B<Genome> objects for the genus specified in the variable
794  $genus.  $genus.
795    
796  C<< $query = $sprout->Get(['Genome'], "Genome(genus) = ?", $genus); >>  C<< $query = $erdb->Get(['Genome'], "Genome(genus) = ?", $genus); >>
797    
798  The WHERE clause contains a single question mark, so there is a single additional  The WHERE clause contains a single question mark, so there is a single additional
799  parameter representing the parameter value. It would also be possible to code  parameter representing the parameter value. It would also be possible to code
800    
801  C<< $query = $sprout->Get(['Genome'], "Genome(genus) = \'$genus\'"); >>  C<< $query = $erdb->Get(['Genome'], "Genome(genus) = \'$genus\'"); >>
802    
803  however, this version of the call would generate a syntax error if there were any quote  however, this version of the call would generate a syntax error if there were any quote
804  characters inside the variable C<$genus>.  characters inside the variable C<$genus>.
# Line 796  Line 810 
810  It is possible to specify multiple entity and relationship names in order to retrieve more than  It is possible to specify multiple entity and relationship names in order to retrieve more than
811  one object's data at the same time, which allows highly complex joined queries. For example,  one object's data at the same time, which allows highly complex joined queries. For example,
812    
813  C<< $query = $sprout->Get(['Genome', 'ComesFrom', 'Source'], "Genome(genus) = ?", $genus); >>  C<< $query = $erdb->Get(['Genome', 'ComesFrom', 'Source'], "Genome(genus) = ?", $genus); >>
814    
815  If multiple names are specified, then the query processor will automatically determine a  If multiple names are specified, then the query processor will automatically determine a
816  join path between the entities and relationships. The algorithm used is very simplistic.  join path between the entities and relationships. The algorithm used is very simplistic.
# Line 959  Line 973 
973              $command .= " ORDER BY $orderClause";              $command .= " ORDER BY $orderClause";
974          }          }
975      }      }
976      Trace("SQL query: $command") if T(2);      Trace("SQL query: $command") if T(3);
977      Trace("PARMS: '" . (join "', '", @params) . "'") if (T(3) && (@params > 0));      Trace("PARMS: '" . (join "', '", @params) . "'") if (T(4) && (@params > 0));
978      my $sth = $dbh->prepare_command($command);      my $sth = $dbh->prepare_command($command);
979      # Execute it with the parameters bound in.      # Execute it with the parameters bound in.
980      $sth->execute(@params) || Confess("SELECT error" . $sth->errstr());      $sth->execute(@params) || Confess("SELECT error" . $sth->errstr());
# Line 971  Line 985 
985    
986  =head3 GetList  =head3 GetList
987    
988  C<< my @dbObjects = $database->GetList(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>  C<< my @dbObjects = $erdb->GetList(\@objectNames, $filterClause, $param1, $param2, ..., $paramN); >>
989    
990  Return a list of object descriptors for the specified objects as determined by the  Return a list of object descriptors for the specified objects as determined by the
991  specified filter clause.  specified filter clause.
# Line 1034  Line 1048 
1048    
1049  =head3 ComputeObjectSentence  =head3 ComputeObjectSentence
1050    
1051  C<< my $sentence = $database->ComputeObjectSentence($objectName); >>  C<< my $sentence = $erdb->ComputeObjectSentence($objectName); >>
1052    
1053  Check an object name, and if it is a relationship convert it to a relationship sentence.  Check an object name, and if it is a relationship convert it to a relationship sentence.
1054    
# Line 1069  Line 1083 
1083    
1084  =head3 DumpRelations  =head3 DumpRelations
1085    
1086  C<< $database->DumpRelations($outputDirectory); >>  C<< $erdb->DumpRelations($outputDirectory); >>
1087    
1088  Write the contents of all the relations to tab-delimited files in the specified directory.  Write the contents of all the relations to tab-delimited files in the specified directory.
1089  Each file will have the same name as the relation dumped, with an extension of DTX.  Each file will have the same name as the relation dumped, with an extension of DTX.
# Line 1111  Line 1125 
1125    
1126  =head3 InsertObject  =head3 InsertObject
1127    
1128  C<< my $ok = $database->InsertObject($objectType, \%fieldHash); >>  C<< my $ok = $erdb->InsertObject($objectType, \%fieldHash); >>
1129    
1130  Insert an object into the database. The object is defined by a type name and then a hash  Insert an object into the database. The object is defined by a type name and then a hash
1131  of field names to values. Field values in the primary relation are represented by scalars.  of field names to values. Field values in the primary relation are represented by scalars.
# Line 1120  Line 1134 
1134  example, the following line inserts an inactive PEG feature named C<fig|188.1.peg.1> with aliases  example, the following line inserts an inactive PEG feature named C<fig|188.1.peg.1> with aliases
1135  C<ZP_00210270.1> and C<gi|46206278>.  C<ZP_00210270.1> and C<gi|46206278>.
1136    
1137  C<< $database->InsertObject('Feature', { id => 'fig|188.1.peg.1', active => 0, feature-type => 'peg', alias => ['ZP_00210270.1', 'gi|46206278']}); >>  C<< $erdb->InsertObject('Feature', { id => 'fig|188.1.peg.1', active => 0, feature-type => 'peg', alias => ['ZP_00210270.1', 'gi|46206278']}); >>
1138    
1139  The next statement inserts a C<HasProperty> relationship between feature C<fig|158879.1.peg.1> and  The next statement inserts a C<HasProperty> relationship between feature C<fig|158879.1.peg.1> and
1140  property C<4> with an evidence URL of C<http://seedu.uchicago.edu/query.cgi?article_id=142>.  property C<4> with an evidence URL of C<http://seedu.uchicago.edu/query.cgi?article_id=142>.
1141    
1142  C<< $database->InsertObject('HasProperty', { 'from-link' => 'fig|158879.1.peg.1', 'to-link' => 4, evidence = 'http://seedu.uchicago.edu/query.cgi?article_id=142'}); >>  C<< $erdb->InsertObject('HasProperty', { 'from-link' => 'fig|158879.1.peg.1', 'to-link' => 4, evidence = 'http://seedu.uchicago.edu/query.cgi?article_id=142'}); >>
1143    
1144  =over 4  =over 4
1145    
# Line 1250  Line 1264 
1264    
1265  =head3 LoadTable  =head3 LoadTable
1266    
1267  C<< my %results = $database->LoadTable($fileName, $relationName, $truncateFlag); >>  C<< my %results = $erdb->LoadTable($fileName, $relationName, $truncateFlag); >>
1268    
1269  Load data from a tab-delimited file into a specified table, optionally re-creating the table  Load data from a tab-delimited file into a specified table, optionally re-creating the table
1270  first.  first.
# Line 1291  Line 1305 
1305      # Check the truncation flag.      # Check the truncation flag.
1306      if ($truncateFlag) {      if ($truncateFlag) {
1307          Trace("Creating table $relationName") if T(2);          Trace("Creating table $relationName") if T(2);
1308            # Compute the row count estimate. We take the size of the load file,
1309            # divide it by the estimated row size, and then multiply by 1.5 to
1310            # leave extra room. We postulate a minimum row count of 1000 to
1311            # prevent problems with incoming empty load files.
1312            my $rowSize = $self->EstimateRowSize($relationName);
1313            my $fileSize = -s $fileName;
1314            my $estimate = FIG::max($fileSize * 1.5 / $rowSize, 1000);
1315          # Re-create the table without its index.          # Re-create the table without its index.
1316          $self->CreateTable($relationName, 0);          $self->CreateTable($relationName, 0, $estimate);
1317          # If this is a pre-index DBMS, create the index here.          # If this is a pre-index DBMS, create the index here.
1318          if ($dbh->{_preIndex}) {          if ($dbh->{_preIndex}) {
1319              eval {              eval {
# Line 1379  Line 1400 
1400                  $retVal->AddMessage($@);                  $retVal->AddMessage($@);
1401              }              }
1402          }          }
1403            # Analyze the table to help optimize tables.
1404      }      }
1405      # Commit the database changes.      # Commit the database changes.
1406      $dbh->commit_tran;      $dbh->commit_tran;
1407        $dbh->vacuum_it($relationName);
1408      # Delete the temporary file.      # Delete the temporary file.
1409      unlink $tempName;      unlink $tempName;
1410      # Return the statistics.      # Return the statistics.
# Line 1390  Line 1413 
1413    
1414  =head3 GenerateEntity  =head3 GenerateEntity
1415    
1416  C<< my $fieldHash = $database->GenerateEntity($id, $type, \%values); >>  C<< my $fieldHash = $erdb->GenerateEntity($id, $type, \%values); >>
1417    
1418  Generate the data for a new entity instance. This method creates a field hash suitable for  Generate the data for a new entity instance. This method creates a field hash suitable for
1419  passing as a parameter to L</InsertObject>. The ID is specified by the callr, but the rest  passing as a parameter to L</InsertObject>. The ID is specified by the callr, but the rest
# Line 1448  Line 1471 
1471    
1472  =head3 GetEntity  =head3 GetEntity
1473    
1474  C<< my $entityObject = $sprout->GetEntity($entityType, $ID); >>  C<< my $entityObject = $erdb->GetEntity($entityType, $ID); >>
1475    
1476  Return an object describing the entity instance with a specified ID.  Return an object describing the entity instance with a specified ID.
1477    
# Line 1484  Line 1507 
1507    
1508  =head3 GetEntityValues  =head3 GetEntityValues
1509    
1510  C<< my @values = GetEntityValues($entityType, $ID, \@fields); >>  C<< my @values = $erdb->GetEntityValues($entityType, $ID, \@fields); >>
1511    
1512  Return a list of values from a specified entity instance.  Return a list of values from a specified entity instance.
1513    
# Line 1527  Line 1550 
1550    
1551  =head3 GetAll  =head3 GetAll
1552    
1553  C<< my @list = $sprout->GetAll(\@objectNames, $filterClause, \@parameters, \@fields, $count); >>  C<< my @list = $erdb->GetAll(\@objectNames, $filterClause, \@parameters, \@fields, $count); >>
1554    
1555  Return a list of values taken from the objects returned by a query. The first three  Return a list of values taken from the objects returned by a query. The first three
1556  parameters correspond to the parameters of the L</Get> method. The final parameter is  parameters correspond to the parameters of the L</Get> method. The final parameter is
# Line 1543  Line 1566 
1566  spreadsheet cell, and each feature will be represented by a list containing the  spreadsheet cell, and each feature will be represented by a list containing the
1567  feature ID followed by all of its aliases.  feature ID followed by all of its aliases.
1568    
1569  C<< $query = $sprout->Get(['ContainsFeature', 'Feature'], "ContainsFeature(from-link) = ?", [$ssCellID], ['Feature(id)', 'Feature(alias)']); >>  C<< $query = $erdb->Get(['ContainsFeature', 'Feature'], "ContainsFeature(from-link) = ?", [$ssCellID], ['Feature(id)', 'Feature(alias)']); >>
1570    
1571  =over 4  =over 4
1572    
# Line 1612  Line 1635 
1635      return @retVal;      return @retVal;
1636  }  }
1637    
1638    =head3 EstimateRowSize
1639    
1640    C<< my $rowSize = $erdb->EstimateRowSize($relName); >>
1641    
1642    Estimate the row size of the specified relation. The estimated row size is computed by adding
1643    up the average length for each data type.
1644    
1645    =over 4
1646    
1647    =item relName
1648    
1649    Name of the relation whose estimated row size is desired.
1650    
1651    =item RETURN
1652    
1653    Returns an estimate of the row size for the specified relation.
1654    
1655    =back
1656    
1657    =cut
1658    #: Return Type $;
1659    sub EstimateRowSize {
1660        # Get the parameters.
1661        my ($self, $relName) = @_;
1662        # Declare the return variable.
1663        my $retVal = 0;
1664        # Find the relation descriptor.
1665        my $relation = $self->_FindRelation($relName);
1666        # Get the list of fields.
1667        for my $fieldData (@{$relation->{Fields}}) {
1668            # Get the field type and add its length.
1669            my $fieldLen = $TypeTable{$fieldData->{type}}->{avgLen};
1670            $retVal += $fieldLen;
1671        }
1672        # Return the result.
1673        return $retVal;
1674    }
1675    
1676  =head2 Internal Utility Methods  =head2 Internal Utility Methods
1677    
1678  =head3 GetLoadStats  =head3 GetLoadStats
# Line 1987  Line 2048 
2048  sub _LoadMetaData {  sub _LoadMetaData {
2049      # Get the parameters.      # Get the parameters.
2050      my ($filename) = @_;      my ($filename) = @_;
2051        Trace("Reading Sprout DBD from $filename.") if T(2);
2052      # Slurp the XML file into a variable. Extensive use of options is used to insure we      # Slurp the XML file into a variable. Extensive use of options is used to insure we
2053      # get the exact structure we want.      # get the exact structure we want.
2054      my $metadata = XML::Simple::XMLin($filename,      my $metadata = XML::Simple::XMLin($filename,
# Line 2014  Line 2076 
2076      for my $entityName (keys %{$entityList}) {      for my $entityName (keys %{$entityList}) {
2077          my $entityStructure = $entityList->{$entityName};          my $entityStructure = $entityList->{$entityName};
2078          #          #
2079          # The first step is to run creating all the entity's default values. For C<Field> elements,          # The first step is to create all the entity's default values. For C<Field> elements,
2080          # the relation name must be added where it is not specified. For relationships,          # the relation name must be added where it is not specified. For relationships,
2081          # the B<from-link> and B<to-link> fields must be inserted, and for entities an B<id>          # the B<from-link> and B<to-link> fields must be inserted, and for entities an B<id>
2082          # field must be added to each relation. Finally, each field will have a C<PrettySort> attribute          # field must be added to each relation. Finally, each field will have a C<PrettySort> attribute
# Line 2193  Line 2255 
2255          my @fromList = ();          my @fromList = ();
2256          my @toList = ();          my @toList = ();
2257          my @bothList = ();          my @bothList = ();
2258          Trace("Join table build for $entityName.") if T(3);          Trace("Join table build for $entityName.") if T(4);
2259          for my $relationshipName (keys %{$relationshipList}) {          for my $relationshipName (keys %{$relationshipList}) {
2260              my $relationship = $relationshipList->{$relationshipName};              my $relationship = $relationshipList->{$relationshipName};
2261              # Determine if this relationship has our entity in one of its link fields.              # Determine if this relationship has our entity in one of its link fields.
2262              my $fromEntity = $relationship->{from};              my $fromEntity = $relationship->{from};
2263              my $toEntity = $relationship->{to};              my $toEntity = $relationship->{to};
2264              Trace("Join check for relationship $relationshipName from $fromEntity to $toEntity.") if T(3);              Trace("Join check for relationship $relationshipName from $fromEntity to $toEntity.") if T(4);
2265              if ($fromEntity eq $entityName) {              if ($fromEntity eq $entityName) {
2266                  if ($toEntity eq $entityName) {                  if ($toEntity eq $entityName) {
2267                      # Here the relationship is recursive.                      # Here the relationship is recursive.
2268                      push @bothList, $relationshipName;                      push @bothList, $relationshipName;
2269                      Trace("Relationship $relationshipName put in both-list.") if T(3);                      Trace("Relationship $relationshipName put in both-list.") if T(4);
2270                  } else {                  } else {
2271                      # Here the relationship comes from the entity.                      # Here the relationship comes from the entity.
2272                      push @fromList, $relationshipName;                      push @fromList, $relationshipName;
2273                      Trace("Relationship $relationshipName put in from-list.") if T(3);                      Trace("Relationship $relationshipName put in from-list.") if T(4);
2274                  }                  }
2275              } elsif ($toEntity eq $entityName) {              } elsif ($toEntity eq $entityName) {
2276                  # Here the relationship goes to the entity.                  # Here the relationship goes to the entity.
2277                  push @toList, $relationshipName;                  push @toList, $relationshipName;
2278                  Trace("Relationship $relationshipName put in to-list.") if T(3);                  Trace("Relationship $relationshipName put in to-list.") if T(4);
2279              }              }
2280          }          }
2281          # Create the nonrecursive joins. Note that we build two hashes for running          # Create the nonrecursive joins. Note that we build two hashes for running
# Line 2259  Line 2321 
2321                  # relationship can only be ambiguous with another recursive relationship,                  # relationship can only be ambiguous with another recursive relationship,
2322                  # and the incoming relationship from the outer loop is never recursive.                  # and the incoming relationship from the outer loop is never recursive.
2323                  for my $otherName (@bothList) {                  for my $otherName (@bothList) {
2324                      Trace("Setting up relationship joins to recursive relationship $otherName with $relationshipName.") if T(3);                      Trace("Setting up relationship joins to recursive relationship $otherName with $relationshipName.") if T(4);
2325                      # Join from the left.                      # Join from the left.
2326                      $joinTable{"$relationshipName/$otherName"} =                      $joinTable{"$relationshipName/$otherName"} =
2327                          "$linkField = $otherName.from_link";                          "$linkField = $otherName.from_link";
# Line 2274  Line 2336 
2336          # rise to situations where we can't create the path we want; however, it is always          # rise to situations where we can't create the path we want; however, it is always
2337          # possible to get the same effect using multiple queries.          # possible to get the same effect using multiple queries.
2338          for my $relationshipName (@bothList) {          for my $relationshipName (@bothList) {
2339              Trace("Setting up entity joins to recursive relationship $relationshipName with $entityName.") if T(3);              Trace("Setting up entity joins to recursive relationship $relationshipName with $entityName.") if T(4);
2340              # Join to the entity from each direction.              # Join to the entity from each direction.
2341              $joinTable{"$entityName/$relationshipName"} =              $joinTable{"$entityName/$relationshipName"} =
2342                  "$entityName.id = $relationshipName.from_link";                  "$entityName.id = $relationshipName.from_link";
# Line 2325  Line 2387 
2387      # index descriptor does not exist, it will be created automatically so we can add      # index descriptor does not exist, it will be created automatically so we can add
2388      # the field to it.      # the field to it.
2389      unshift @{$newIndex->{IndexFields}}, $firstField;      unshift @{$newIndex->{IndexFields}}, $firstField;
2390        # If this is a one-to-many relationship, the "To" index is unique.
2391        if ($relationshipStructure->{arity} eq "1M" && $indexKey eq "To") {
2392            $newIndex->{Unique} = 'true';
2393        }
2394      # Add the index to the relation.      # Add the index to the relation.
2395      _AddIndex("idx$relationshipName$indexKey", $relationStructure, $newIndex);      _AddIndex("idx$relationshipName$indexKey", $relationStructure, $newIndex);
2396  }  }

Legend:
Removed from v.1.11  
changed lines
  Added in v.1.19

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3