Hierarchy Queries

Creating a Transitive Closure to Optimize Rollups

Steven F. Lott

Table of Contents

Introduction

Data Warehouse Extract Transform and Load (ETL) processing requires loading hierarchy descriptions. These include chart-of-accounts (COA), organization, geography as examples. These hierarchies are not easily dictated like a time hierarchy; these are discovered from hierarchies in other reporting applications.

A hierarchy is a transitive relationship between a parent and a number of children. In a relational database this is most often modeled as children with a foreign key references to their common parent.

If this transitive relationship is of arbitrary depth, it is notoriously difficult to process in SQL. Each query requires an arbitrary number of joins to locate all parents of a child or all children of a parent. The usual work-around is to limit the depth (for example to 8 levels); this limits the queries to at most 8 joins.

Another common solution is to create a "transitive closure". This "closes" or pre-joins all levels of the hierarchy, creating all possible paths. This transitive closure can be used for all queries using a single SELECT to find all parents of a child or all children of a parent. We will assign "subtree range numbers" to each node during the transitive closure. These subtree range numbers are based on the Heap data structure described in the Cormen/Leiserson/Rivest/Stein: Introduction to Algorithms.

The only constraint on the transitive closure is that it is kept in synchronization with the actual parent-child relationships. When a hierarchy is changed, the transitive closure must be recomputed.

This document presents overall requirements, design of the transitive closure algorith, and the implementation of both transitive closure and the data generator.

Requirements

This demonstration creates a transitive closure on hierarchical data kept in a relational database. The parent-child table is scanned to create the transitive closure in a separate table.

Proper subtree range numbers have the following properties. Assume a three level tree of g, p1, p2, and c1a, c1b. Each node has two attributes, l and h.

The tree has the following form.


             g
            / \
           p1  p2
          / \
        c1a  c1b
    

The following strict ordering applies among the subtree numbers:

g.l <= p1.l <= c1a.l <= c1a.h < c1b.l <= c1b.h <= p1.h < p2.l <= p2.h <= g.h

The parent-child relationship can be summarized as p.l <= all children <= p.h.

This leads to very simple queries for locating parents or children:

By adding an additional attribute of level number, we can locate the immediate parent or child of a node, using the relationship parent.level+1 = child.level. Given the immediate parent, we can locate siblings (all nodes of our level that share our immediate parent).

Two separate programs are defined. One creates the database and then loads a hierarchy into the pc table. For a quick supply of sample data, a file directory structure is scanned to load a hierarchy. The other program scans the parent-child table and creates the transitive closure.

Design Overview

The essence of transitive closure is a traversal of the parent-child tree. This can be based on the Visitor design pattern. The Visitor patterns suggests that we design a class to contain the structure being visited and a separate family of related classes for the operations that will be performed on the various elements of this structure.

The structure that will be visited is the parent-child hierarchy represented in the relational database. We define a TreeTable class to contain the basic operations for working with this structure. This class also provides the basic visit methods. The are a variety of alternative orderings to the elements of a structure. In our case, we are interested in traversal that visits each parent node before visiting all the children and after visiting all the children. This could be called a pre-and-post-order depth-first traversal.

There are a wide variety of possible operations that can be performed when traversing the hierarchy. We will focus on two: reporting and assigning subtree range numbers. The reporting operation simply prints each node in the hierarchy. The assignment of subtree range numbers is done by saving the first number on descending to a child node, and the last number on ascending from a child node. This is used to set the number of a parent node.

A simple extension allows selective renumbering of a portion of a tree. This must be done after an insert; it can be done to balance numbers after a delete. The procedure is as follows.

  1. Identify the immediate parent of the change, get the existing range of values, p.l and p.h.
  2. Count the actual nodes which occur in this parent's subtree, n.
  3. Divide the available range by the number of nodes to determine the spacing of numbers, s.
  4. Perform the transitive closure, assigning numbers from p.l, increasing by s.

Main Program

The main program can have the following general form.


f= TreeTable("hier.db")
s= SetNumbers(5)
f.prep(s)
f.visit(s)
f.finish(s)
    

A TreeTable instance, f includes a database connection to the named database. A SetNumbers visitor instance, s is used to assign subtree numbers. The prep method clears the transitive closure result table. The visit step applies the visitor, s, to each node in the tree. The finish step commits the result.

Implementation

This small demonstration was implemented as two Python programs: hierarchyDB.py and transclose.py. The hierarchyDB.py loads a hierarchy database by examining a random disk directory. The transclose.py updates that hierarchy database by creating the transitive closure information.

hierarchyDB.py

The hierarchyDB.py program creates test data. It defines the SampleDB class which contains methods to create and load a database. The main driver uses an instance of this class to create and load the database.

This program has the following structure.

"hierarchyDB.py" (1) =


DB Shell Escape (4) 
DB DOC String (2) 
DB CVS Cruft and pyweb generator warning (5) 
DB Imports (3) 
DB SampleDB class defines methods to create and load a database (6) 
DB Main Driver to create and load our test database (9) 

"hierarchyDB.py" (1).

Overheads

Overheads includes the following: the shell escape, the doc string, any CVS cruft, and the imports.

DOC string

The doc string identifies the module source.

DB DOC String (2) =


"""HierarchyDB.py

Create a hierarchy database by traversing a directory tree.
"""

DB DOC String (2). Used by hierarchyDB.py (1).

Imports

The following imports are used by this module. The os module provides information on a directory tree, used to populate the database. The time module is used to decode the time stamps on each file. The pprint module is used to create nice listings of complex Python data structures. The sqlite module provides a simple relational database.

DB Imports (3) =



import sys, os, time, pprint
from pysqlite2 import dbapi2 as sqlite

DB Imports (3). Used by hierarchyDB.py (1).

Other Overheads

These are other overheads of a Python program: the Shebang line, also know as the Shell Escape, and some CVS Cruft in case this is checked into CVS or SVN or similar tool.

DB Shell Escape (4) =


#!/usr/bin/env python

DB Shell Escape (4). Used by hierarchyDB.py (1).

DB CVS Cruft and pyweb generator warning (5) =


__version__ = """$Revision$"""

### DO NOT EDIT THIS FILE!
### It was created by ..\pyWeb-1.4\pyweb.py, __version__='$Revision$'.
### From source transclose.w modified Fri Jan 05 17:20:22 2007.
### In working directory 'G:\Projects\transclose'.

DB CVS Cruft and pyweb generator warning (5). Used by hierarchyDB.py (1).

SampleDB Class

The SampleDB class performs all of the processing to create and populate a database. This class has the following instance variables and methods.

dbDir
the directory for the database
dbName
the file name of the database
connection
the connection to the database
sequence
a sequence generator used to create surrogate keys

DB SampleDB class defines methods to create and load a database (6) =



class SampleDB( object ):
    """Manage a simple database for transitive closure demonstration."""
    def __init__( self, name_="hierarchy.db" ):
        self.dbName= name_
        self.connection= None
        self.sequence= 0
    def execute( self, statement, bindTuple=() ):
        """Execute a simple non-query statement."""
        cursor= self.connection.cursor()
        try:
            cursor.execute( statement, bindTuple )
        except sqlite.OperationalError, e:
            print e
        cursor.close()
    def query( self, statement ):
        """Execute a simple query."""
        cursor= self.connection.cursor()
        cursor.execute( statement )
        for row in cursor.fetchall():
            print row
        cursor.close()
    DB SampleDB Create the empty database (7) 
    DB SampleDB Load the database by visiting a disk structure (8) 

DB SampleDB class defines methods to create and load a database (6). Used by hierarchyDB.py (1).

Creating an empty database is a matter of creating the parent-child table, pc, and the table to contain the transitive closure information, trans.

.

DB SampleDB Create the empty database (7) =



def create( self ):
    """Create the database and schema."""
    self.connection= sqlite.connect(self.dbName)
    self.execute( """CREATE TABLE pc( key integer,
        dirName varchar, fileName varchar, mtime varchar, size integer )""" )
    self.execute( """CREATE TABLE trans( key integer,
        level integer, low integer, high integer )""" )
    self.connection.commit()
    #self.query( """PRAGMA database_list""" )
    self.connection.close()
    print "Create Complete",self.dbName

DB SampleDB Create the empty database (7). Used by DB SampleDB class defines methods to create and load a database (6) :: hierarchyDB.py (1).

Loading the essential parent-child table, trans involves a little hand-shake with Python's os.path.walk function. The os.path.walk function does a callback with each directory in the tree, and a list of all files in that directory. The SampleDB.visit method is the required callback function, used by os.path.walk.

This callback function gathers some basic file system information, and inserts that information into the pc table. It collects modification time and size of each file.

DB SampleDB Load the database by visiting a disk structure (8) =



def load( self, fromDirectory ):
    """Load the parent-child tree into the pc table."""
    self.connection= sqlite.connect(self.dbName)
    # traverse a directory tree to create some sample data
    os.path.walk( fromDirectory, SampleDB.visit, self )
    self.connection.commit()
    self.query( """select * from pc""" )
    self.connection.commit()
    self.connection.close()
    print "Load Complete",self.dbName
def visit( self, dirname, names ):
    """A quick hack that would fail if a directory name matched a file name."""
    #print dirname
    #pprint.pprint( names )
    path,base=os.path.split(dirname)
    for f in names:
        mTime= os.path.getmtime( os.path.join(dirname,f) )
        if mTime <= 0: continue # Skip files with odd timestamps
        mTimeStr= time.asctime( time.localtime( mTime ) )
        size= os.path.getsize( os.path.join(dirname,f) )
        self.sequence += 1
        self.execute( """insert into pc(key,dirName,fileName,mtime,size)
            values(?,?,?,?,?)""",
            (self.sequence,base,f,mTimeStr,size) )

DB SampleDB Load the database by visiting a disk structure (8). Used by DB SampleDB class defines methods to create and load a database (6) :: hierarchyDB.py (1).

Note that there is a subtle bug in database design and the load method. This design has a file using the parent directory's name as the foreign key. It should use the assigned surrogate key instead. File and directory names are not guaranteed to be unique, and the surrogate key is essential to correctly constructing a tree representing directories.

Test Driver

The main driver creates a database and loads it. It sets s to an instance of SampleDB. It then invokes the create method to create a new, empty database. It invokes the load method to populate this database using the L: drive or ~/Documents directory structure.

DB Main Driver to create and load our test database (9) =



def main():
    s= SampleDB("hier.db")
    s.create()
    rootDirectory= ".."
    s.load(rootDirectory)

if __name__ == "__main__":
    main()

DB Main Driver to create and load our test database (9). Used by hierarchyDB.py (1).

transclose.py

The transclose.py program demonstrates the transitive closure algorithm. This program defines the basic TreeTable class that manages a parent-child tree structure in a relational table. The program also defines a Visitor class hierarchy that can be used to construct a transitive closure of a tree structure, assigning subtree numbers to speed up processing.

This program has the following structure.

"transclose.py" (10) =


TC Shell Escape (13) 
TC DOC String (11) 
TC CVS Cruft and pyweb generator warning (14) 
TC Imports (12) 

TC Visitor base class definition (15) 
TC TreeTable class to process a parent-child relationship table (16) 
TC ShowDirectory subclass of Visitor to report on a structure (18) 
TC SetNumbers subclass of Visitor to assign numbers (19) 

TC Main Driver (20) 

"transclose.py" (10).

Overheads

These are some standard overheads of Python programs.

DOC string

This is the doc string which appears in the source.

TC DOC String (11) =


"""Transitive Closure Demo.

This application does a transitive closure on a hierarchy.

This will do a depth-first traversal of the data, assigning node numbers in such
a way that for a given node, n, the following are true.

If c is a child of n, then n.low <= c.low <= c.high <= n.high.

If p is a parent of n, then p.low <= n.low <= n.high <= p.high.

This allows simple SQL queries to locate all children or all parents of a given node.

>"""

TC DOC String (11). Used by transclose.py (10).

Imports

The following imports are used by this module. The os module is used to separate the database name into path and filename components. The sqlite module provides a simple relational database.

TC Imports (12) =



import os
from pysqlite2 import dbapi2 as sqlite

TC Imports (12). Used by transclose.py (10).

Other Overheads

These are other overheads of a Python program: the Shebang line, also know as the Shell Escape, and some CVS Cruft in case this is checked into CVS or SVN or similar tool.

TC Shell Escape (13) =


#!/usr/local/bin/python

TC Shell Escape (13). Used by transclose.py (10).

TC CVS Cruft and pyweb generator warning (14) =


__version__ = """$Revision$"""

### DO NOT EDIT THIS FILE!
### It was created by ..\pyWeb-1.4\pyweb.py, __version__='$Revision$'.
### From source transclose.w modified Fri Jan 05 17:20:22 2007.
### In working directory 'G:\Projects\transclose'.

TC CVS Cruft and pyweb generator warning (14). Used by transclose.py (10).

Visitor Base Class

The Visitor base class provides the superclass for all TreeTable visitors.

This class has the following instance variables.

step
the step size for numbering the subtrees. By default it is 1, but this can limit the ability to add nodes without renumbering.
count
the count of parent and children. This is assigned a unique number for each parent and each child. It also skips one step at the end of each list of children.
stack
the current numbering context, used to remember the starting subtree number when descending through the hierarchy.

TC Visitor base class definition (15) =



class Visitor( object ):
    def __init__( self, step=1 ):
        self.step= step
        self.count= 0
        self.stack= [0]
    def prep( self, connection ):
        """Override this to prepare for visiting."""
        pass
    def finish( self, connection ):
        """Override this to finalize after visiting."""
        pass
    def depth( self ):
        return len(self.stack)
    def start( self ):
        return self.stack[-1]
    def end( self ):
        return self.count+self.step # leave a gap after my last child
    def leaf( self, *args ):
        self.count += self.step
    def enter( self, *args ):
        self.count += self.step # me
        self.stack.append( self.count+self.step ) # my first child
        return 1 # to descend
    def exit( self, *args ):
        self.count += self.step
        self.stack.pop()

TC Visitor base class definition (15). Used by transclose.py (10).

TreeTable Class

The TreeTable class defines several functions for working with a tree defined in a relational table.

dbDir
the directory for the database
dbName
the file name of the database
connection
the connection to the database

Additionally, there are reporting methods, shown below, that show typical queries based on the transitive closure information built by the SetNumbers visitor.

TC TreeTable class to process a parent-child relationship table (16) =



class TreeTable( object ):
    def __init__( self, name_=r"hierarchy.db" ):
        self.dbName= name_
        self.connection= sqlite.connect(self.dbName)
    def hasChildren( self, fromDir="" ):
        c= self.connection.cursor()
        c.execute( """SELECT count(*) FROM pc WHERE dirName=?""", (fromDir,) )
        r= c.fetchone()[0]
        c.close()
        #print fromDir, "hasChildren=", r
        return r
    def prep( self, visitor ):
        visitor.prep( self.connection )
    def finish( self, visitor ):
        visitor.finish( self.connection )
    def visit( self, visitor, fromDir="" ):
        #print "visiting",fromDir
        c= self.connection.cursor()
        c.execute( """SELECT key, fileName, mtime, size
            FROM pc
            WHERE dirName=?
            ORDER BY 2""", (fromDir,) )
        #print fromDir,":"
        for row in c.fetchall():
            key, fileName, mtime, size = row
            if self.hasChildren(fileName):
                filter= visitor.enter( self.connection, fromDir, key, fileName )
                if filter:
                    self.visit( visitor, fileName )
                visitor.exit( self.connection, fromDir, key, fileName )
            else:
                visitor.leaf( self.connection, fromDir, key, fileName, mtime, size )
        c.close()

    TC Queries using the transitive closure information (17) 

TC TreeTable class to process a parent-child relationship table (16). Used by transclose.py (10).

TC Queries using the transitive closure information (17) =



def targets( self, aName ):
    sqlTarget= """
        SELECT trans.low, trans.high
        FROM pc, trans
        WHERE pc.fileName=? AND pc.key=trans.key"""
    tgt= self.connection.cursor()
    tgt.execute( sqlTarget, (aName,) )
    tgtList= list( tgt.fetchall() )
    tgt.close()
    #print 'targets:', aName,  '=', tgtList
    return tgtList
def parentsOf( self, aName ):
    sqlParents= """
        SELECT *
        FROM pc, trans
        WHERE ? BETWEEN trans.low AND trans.high
        AND trans.low <> trans.high
        AND pc.key = trans.key
        ORDER BY trans.level ASC"""
    for f,_ in self.targets(aName):
        print aName, f, 'has path:'
        par= self.connection.cursor()
        par.execute( sqlParents, (f,) )
        for p in par.fetchall():
            print p
        par.close()
def childrenOf( self, aName ):
    sqlChildren= """
        SELECT *
        FROM pc, trans
        WHERE trans.low BETWEEN ? AND ?
        AND pc.key = trans.key
        ORDER BY level ASC"""
    for fl,fh in self.targets(aName):
        print aName, 'has children:'
        ch= self.connection.cursor()
        ch.execute( sqlChildren, (fl,fh) )
        for p in ch.fetchall():
            print p
        ch.close()

TC Queries using the transitive closure information (17). Used by TC TreeTable class to process a parent-child relationship table (16) :: transclose.py (10).

ShowDirectory Visitor Subclass

The ShowDirectory class overrides functions in the Visitor base class to write an indented report of the tree structure.

TC ShowDirectory subclass of Visitor to report on a structure (18) =



class ShowDirectory( Visitor ):
    def enter( self, connection, parent, key, child ):
        Visitor.enter( self )
        print self.depth()*' ', child, self.count
        return 1
    def exit( self, connection, parent, key, child ):
        print self.depth()*' ', child, self.start(), self.end()
        Visitor.exit( self )
    def leaf( self, connection, parent, key, child, mtime, size ):
        Visitor.leaf( self )
        print self.depth()*' ', child, self.count, size, mtime

TC ShowDirectory subclass of Visitor to report on a structure (18). Used by transclose.py (10).

SetNumbers Visitor Sublass

The SetNumbers class overrides functions in the Visitor base class to assign subtree range numbers to the tree structure.

TC SetNumbers subclass of Visitor to assign numbers (19) =



class SetNumbers( Visitor ):
    def exit( self, connection, parent, key, child ):
        c= connection.cursor()
        c.execute( """INSERT INTO trans(key,level,low,high)
            values(?,?,?,?)""",
            (key,self.depth(),self.start(),self.end()) )
        c.close()
        Visitor.exit( self )
    def leaf( self, connection, parent, key, child, mtime, size ):
        Visitor.leaf( self )
        c= connection.cursor()
        c.execute( """INSERT INTO trans(key,level,low,high)
            values(?,?,?,?)""",
            (key,self.depth(),self.count,self.count) )
        c.close()
    def prep( self, connection ):
        c= connection.cursor()
        c.execute( """delete from trans""" )
        c.close()
        connection.commit()
    def finish( self, connection ):
        connection.commit()
        cursor= connection.cursor()
        cursor.execute( """select * from trans""" )
        for row in cursor.fetchall():
            print row
        cursor.close()

TC SetNumbers subclass of Visitor to assign numbers (19). Used by transclose.py (10).

Main Driver

The main driver is broken into two functions. The main function assigns numbers to the tree. The report function uses the assigned subtree numbers to report on selected elements of the tree.

The main function creates a TreeTable instance, f. It also creates a SetNumbers visitor, s, which populates the trans table with the transitive closure of the tree in the tree table. The basic visit procedure is to call the TreeTable's prep, visit and finish for the given visitor.

The report function creates a TreeTable instance, f. It requests the parents of "__init__.py"; this file occurs several times, with separate different lists of parents. It requests the children of "test".

TC Main Driver (20) =



def main():
    f= TreeTable("hier.db")
    print "directory tree"
    s= SetNumbers(5)
    f.prep(s)
    f.visit(s,"..")
    f.finish(s)

def report():
    f= TreeTable("hier.db")
    print "reports"
    f.parentsOf( "pyweb.css" )
    f.childrenOf( "test" )

if __name__ == "__main__":
    main()
    report()

TC Main Driver (20). Used by transclose.py (10).

Output Logs

Hierarchy Creation


Create Complete hier.db
(1, u'..', u'htmlparse', u'Mon Sep 12 15:05:40 2005', 0)
(2, u'..', u'pyWeb-1.4', u'Mon Sep 12 15:13:46 2005', 0)
...
(10, u'..', u'transclose', u'Fri Jan 05 10:55:10 2007', 0)
(11, u'htmlparse', u'CMD.EXE.lnk', u'Wed Nov 02 13:32:54 2005', 425)
(12, u'htmlparse', u'htmlparse.tws', u'Mon Jun 21 15:17:02 2004', 14761)
(13, u'htmlparse', u'htmlparsetree-1.3.zip', u'Thu Sep 11 20:17:14 2003', 54795)
...
(78, u'pyWeb-1.4', u'example.html', u'Thu Mar 03 17:57:22 2005', 788)
(79, u'pyWeb-1.4', u'example.w', u'Thu Mar 03 17:57:22 2005', 253)
(80, u'pyWeb-1.4', u'index.html', u'Thu Mar 03 17:57:22 2005', 247674)
...
(388, u'pyWeb-1.5', u'test', u'Fri Jan 05 10:50:38 2007', 0)
...
(394, u'test', u'example.w', u'Thu Mar 03 17:57:22 2005', 253)
(395, u'test', u'source.log', u'Thu Mar 03 17:57:42 2005', 160)
(396, u'test', u'test0.w', u'Thu Mar 03 17:57:42 2005', 300)
(397, u'test', u'test1.w', u'Thu Mar 03 17:57:42 2005', 158)
...
(433, u'transclose', u'hier.db', u'Fri Jan 05 17:05:20 2007', 3072)
(434, u'transclose', u'hierarchy.log', u'Fri Jan 05 17:05:20 2007', 0)
...
Load Complete hier.db

Transitive Closure Processing



reports
pyweb.css 2260 has path:
(1, u'..', u'htmlparse', u'Mon Sep 12 15:05:40 2005', 0, 1, 2, 1215, 2390)
pyweb.css 2615 has path:
(2, u'..', u'pyWeb-1.4', u'Mon Sep 12 15:13:46 2005', 0, 2, 2, 2405, 2770)
pyweb.css 2570 has path:
(2, u'..', u'pyWeb-1.4', u'Mon Sep 12 15:13:46 2005', 0, 2, 2, 2405, 2770)
(92, u'pyWeb-1.4', u'pyweb Folder', u'Mon Sep 12 15:13:28 2005', 0, 92, 3, 2555, 2590)
pyweb.css 2890 has path:
(9, u'..', u'pyWeb-1.5', u'Fri Jan 05 10:50:36 2007', 0, 9, 2, 2780, 3110)
pyweb.css 5005 has path:
(10, u'..', u'transclose', u'Fri Jan 05 10:55:10 2007', 0, 10, 2, 3140, 5040)
test has children:
(388, u'pyWeb-1.5', u'test', u'Fri Jan 05 10:50:38 2007', 0, 388, 3, 2925, 3080)
(394, u'test', u'example.w', u'Thu Mar 03 17:57:22 2005', 253, 394, 3, 2935, 2935)
(395, u'test', u'source.log', u'Thu Mar 03 17:57:42 2005', 160, 395, 3, 2945, 2945)
(396, u'test', u'test0.w', u'Thu Mar 03 17:57:42 2005', 300, 396, 3, 2950, 2950)
(397, u'test', u'test1.w', u'Thu Mar 03 17:57:42 2005', 158, 397, 3, 2955, 2955)
(398, u'test', u'test2.w', u'Thu Mar 03 17:57:42 2005', 127, 398, 3, 2960, 2960)
(399, u'test', u'test3.w', u'Thu Mar 03 17:57:42 2005', 204, 399, 3, 2965, 2965)
(400, u'test', u'test4.w', u'Thu Mar 03 17:57:42 2005', 171, 400, 3, 2970, 2970)
(401, u'test', u'test5.w', u'Thu Mar 03 17:57:42 2005', 206, 401, 3, 2975, 2975)
(402, u'test', u'test6.tex', u'Thu Mar 03 17:57:42 2005', 1194, 402, 3, 2980, 2980)
(403, u'test', u'test6.tmp', u'Thu Mar 03 17:57:44 2005', 36, 403, 3, 2985, 2985)
(404, u'test', u'test6.w', u'Thu Mar 03 17:57:44 2005', 212, 404, 3, 2990, 2990)
(405, u'test', u'test7.tex', u'Thu Mar 03 17:57:44 2005', 149, 405, 3, 2995, 2995)
(406, u'test', u'test7.w', u'Thu Mar 03 17:57:44 2005', 163, 406, 3, 3000, 3000)
(407, u'test', u'test7a.tex', u'Thu Mar 03 17:57:44 2005', 22, 407, 3, 3005, 3005)
(408, u'test', u'test7a.w', u'Thu Mar 03 17:57:44 2005', 22, 408, 3, 3010, 3010)
(409, u'test', u'test8.tex', u'Thu Mar 03 17:57:44 2005', 111, 409, 3, 3015, 3015)
(410, u'test', u'test8.w', u'Thu Mar 03 17:57:44 2005', 163, 410, 3, 3020, 3020)
(411, u'test', u'test8a.w', u'Thu Mar 03 17:57:46 2005', 90, 411, 3, 3025, 3025)
(412, u'test', u'test9.tex', u'Thu Mar 03 17:57:46 2005', 276, 412, 3, 3030, 3030)
(413, u'test', u'test9.w', u'Thu Mar 03 17:57:46 2005', 184, 413, 3, 3035, 3035)
(565, u'test', u'__init__.py', u'Tue May 07 20:39:16 2002', 2164, 565, 3, 2925, 2925)
(566, u'test', u'__init__.pyc', u'Sat Mar 15 14:29:40 2003', 1147, 566, 3, 2930, 2930)
(567, u'test', u'gfstest.py', u'Sat May 11 07:18:50 2002', 6538, 567, 3, 2940, 2940)
(568, u'test', u'test_gadfly.py', u'Sat May 18 20:54:24 2002', 43432, 568, 3, 3040, 3040)
(569, u'test', u'test_gadfly.pyc', u'Sat Mar 15 14:29:40 2003', 49301, 569, 3, 3045, 3045)
(570, u'test', u'test_kjbuckets.py', u'Tue May 07 20:39:16 2002', 9790, 570, 3, 3060, 3060)
(571, u'test', u'test_kjbuckets.pyc', u'Sat Mar 15 14:29:40 2003', 10009, 571, 3, 3065, 3065)
(572, u'test', u'test_kjParseBuild.py', u'Fri May 10 21:59:04 2002', 8087, 572, 3, 3050, 3050)
(573, u'test', u'test_kjParseBuild.pyc', u'Sat Mar 15 14:29:40 2003', 7689, 573, 3, 3055, 3055)
(574, u'test', u'test_sqlgrammar.py', u'Tue May 07 20:39:16 2002', 1968, 574, 3, 3070, 3070)
(575, u'test', u'test_sqlgrammar.pyc', u'Sat Mar 15 14:29:42 2003', 1779, 575, 3, 3075, 3075)
test has children:
(394, u'test', u'example.w', u'Thu Mar 03 17:57:22 2005', 253, 394, 4, 4815, 4815)
(395, u'test', u'source.log', u'Thu Mar 03 17:57:42 2005', 160, 395, 4, 4825, 4825)
(396, u'test', u'test0.w', u'Thu Mar 03 17:57:42 2005', 300, 396, 4, 4830, 4830)
(397, u'test', u'test1.w', u'Thu Mar 03 17:57:42 2005', 158, 397, 4, 4835, 4835)
(398, u'test', u'test2.w', u'Thu Mar 03 17:57:42 2005', 127, 398, 4, 4840, 4840)
(399, u'test', u'test3.w', u'Thu Mar 03 17:57:42 2005', 204, 399, 4, 4845, 4845)
(400, u'test', u'test4.w', u'Thu Mar 03 17:57:42 2005', 171, 400, 4, 4850, 4850)
(401, u'test', u'test5.w', u'Thu Mar 03 17:57:42 2005', 206, 401, 4, 4855, 4855)
(402, u'test', u'test6.tex', u'Thu Mar 03 17:57:42 2005', 1194, 402, 4, 4860, 4860)
(403, u'test', u'test6.tmp', u'Thu Mar 03 17:57:44 2005', 36, 403, 4, 4865, 4865)
(404, u'test', u'test6.w', u'Thu Mar 03 17:57:44 2005', 212, 404, 4, 4870, 4870)
(405, u'test', u'test7.tex', u'Thu Mar 03 17:57:44 2005', 149, 405, 4, 4875, 4875)
(406, u'test', u'test7.w', u'Thu Mar 03 17:57:44 2005', 163, 406, 4, 4880, 4880)
(407, u'test', u'test7a.tex', u'Thu Mar 03 17:57:44 2005', 22, 407, 4, 4885, 4885)
(408, u'test', u'test7a.w', u'Thu Mar 03 17:57:44 2005', 22, 408, 4, 4890, 4890)
(409, u'test', u'test8.tex', u'Thu Mar 03 17:57:44 2005', 111, 409, 4, 4895, 4895)
(410, u'test', u'test8.w', u'Thu Mar 03 17:57:44 2005', 163, 410, 4, 4900, 4900)
(411, u'test', u'test8a.w', u'Thu Mar 03 17:57:46 2005', 90, 411, 4, 4905, 4905)
(412, u'test', u'test9.tex', u'Thu Mar 03 17:57:46 2005', 276, 412, 4, 4910, 4910)
(413, u'test', u'test9.w', u'Thu Mar 03 17:57:46 2005', 184, 413, 4, 4915, 4915)
(458, u'gadfly-1.0.0', u'test', u'Fri Jan 05 10:55:56 2007', 0, 458, 4, 4805, 4960)
(565, u'test', u'__init__.py', u'Tue May 07 20:39:16 2002', 2164, 565, 4, 4805, 4805)
(566, u'test', u'__init__.pyc', u'Sat Mar 15 14:29:40 2003', 1147, 566, 4, 4810, 4810)
(567, u'test', u'gfstest.py', u'Sat May 11 07:18:50 2002', 6538, 567, 4, 4820, 4820)
(568, u'test', u'test_gadfly.py', u'Sat May 18 20:54:24 2002', 43432, 568, 4, 4920, 4920)
(569, u'test', u'test_gadfly.pyc', u'Sat Mar 15 14:29:40 2003', 49301, 569, 4, 4925, 4925)
(570, u'test', u'test_kjbuckets.py', u'Tue May 07 20:39:16 2002', 9790, 570, 4, 4940, 4940)
(571, u'test', u'test_kjbuckets.pyc', u'Sat Mar 15 14:29:40 2003', 10009, 571, 4, 4945, 4945)
(572, u'test', u'test_kjParseBuild.py', u'Fri May 10 21:59:04 2002', 8087, 572, 4, 4930, 4930)
(573, u'test', u'test_kjParseBuild.pyc', u'Sat Mar 15 14:29:40 2003', 7689, 573, 4, 4935, 4935)
(574, u'test', u'test_sqlgrammar.py', u'Tue May 07 20:39:16 2002', 1968, 574, 4, 4950, 4950)
(575, u'test', u'test_sqlgrammar.pyc', u'Sat Mar 15 14:29:42 2003', 1779, 575, 4, 4955, 4955)

Indices

Files

hierarchyDB.py:
1
transclose.py:
10

Macros

DB CVS Cruft and pyweb generator warning:
5
DB DOC String:
2
DB Imports:
3
DB Main Driver to create and load our test database:
9
DB SampleDB Create the empty database:
7
DB SampleDB Load the database by visiting a disk structure:
8
DB SampleDB class defines methods to create and load a database:
6
DB Shell Escape:
4
TC CVS Cruft and pyweb generator warning:
14
TC DOC String:
11
TC Imports:
12
TC Main Driver:
20
TC Queries using the transitive closure information:
17
TC SetNumbers subclass of Visitor to assign numbers:
19
TC Shell Escape:
13
TC ShowDirectory subclass of Visitor to report on a structure:
18
TC TreeTable class to process a parent-child relationship table:
16
TC Visitor base class definition:
15

User Identifiers

SampleDB:
•6 8 9
SetNumbers:
•19 20
ShowDirectory:
•18
TreeTable:
•16 20
Visitor:
•15 18 19
__version__:
5 •14
main:
•9 20
os:
3 8 •12
pprint:
•3 8
report:
•20
sqlite:
3 6 7 8 •12 16
time:
•3 8

Created by ..\pyWeb-1.4\pyweb.py at Fri Jan 05 17:20:25 2007.

pyweb.__version__ '$Revision$'.

Source transclose.w modified Fri Jan 05 17:20:22 2007.

Working directory 'G:\Projects\transclose'.